资讯动态

CHAP验证详解:链路层认证原理、报文流程与安全边界

发布时间:2026/10/27 22:12:58 来源:尧图企业网站定制
干链路层认证这一块的同行看到“CHAP验证”这四个字母大概率不会陌生它出现在宽带拨号的认证阶段出现在专线对接的协商日志里也出现在两台设备背靠背互联的配置清单上。CHAP验证本质上是“挑战-握手认证协议”的落地实现——认证方发一个随机数过来被认证方用自己的密码和这个随机数算一个摘要值回去双方比对这个摘要对上了就放行对不上就断链。它解决的核心问题是在一条不可信的链路上如何在不传输明文密码的前提下证明“我知道密码”。这篇文章适合三类人看一是刚接手拨号链路、被日志里那行认证失败卡住的新手二是想把认证原理彻底搞明白、能自己抓包验算的运维三是需要给设备互联方案做选型、想知道CHAP到底还够不够用的架构人员。下面我按“原理拆解—报文细节—实操配置—安全边界—排错速查”的顺序把这套东西从头到尾讲透中间穿插我自己踩过的坑和能直接抄的配置。1. CHAP验证到底在验证什么把三次报文拆成人话1.1 三个角色和一次完整的握手过程CHAP验证的参与方只有三个认证方Authenticator通常是你拨入的那一端或者链路里负责放行的那台设备、被认证方Peer通常是你这边的拨号客户端或下级设备、以及双方事先约定好的共享密钥Secret口语里常叫“密码”但它和登录密码不是一回事。注意这个三元结构后面九成的故障都出在第三方——密钥上而不是算法本身。认证流程用大白话讲是这样的认证方先甩出一个随机挑战值同时带上自己的名字和一个本次会话的标识符被认证方收到后把标识符、自己的密钥、收到的挑战值这三样东西按固定顺序拼成一串字节丢进摘要算法里算出一个固定长度的摘要值连同自己的名字一起回给认证方认证方手里有同样的密钥它用同样的顺序再算一遍两个摘要值一致就回一个认证成功报文不一致就回认证失败链路随即被拆掉。整个过程中密钥从来没有在链路上出现过链路上跑的只有一个摘要值和一个随机数。这就是CHAP验证相对明文口令认证最本质的进步抓包抓不到密码。当然“抓不到”不等于“攻不破”这个后面第4章会专门讲。1.2 为什么不能只发一次密码明文认证的三个致命伤要理解CHAP验证为什么这么设计得先看它替代的东西。最早期的链路认证是明文口令式的客户端把用户名和密码原样发过去服务端查表比对。这种方式的第一个问题是链路上裸奔任何能在链路上抓到包的人只要把十六进制转成字符密码就到手了而且是一次性永久到手第二个问题是没有时效性抓到的密码明天、下个月、换台设备照样能用第三个问题是无法防重放攻击者甚至不需要知道密码内容把抓到的那个认证报文原封不动再发一遍服务端照样认。CHAP验证针对性地解决了后两个半问题。随机挑战值每次都不一样所以回应的摘要值每次也不一样抓到的旧报文重放上去服务端用新的挑战值一算对不上直接拒掉。至于第一个问题它没有完全解决——如果密钥本身太弱攻击者可以离线暴力穷举这一点在后文会详细展开。注意CHAP验证只解决“证明身份”这一步不解决“传输加密”。认证通过之后链路上跑的业务数据依然是明文的别把认证协议当成加密协议用这是新手最常见的误解。1.3 随机挑战值的设计意图防重放的核心机关挑战值的长度在标准里只要求至少一个字节但实际实现里普遍取16字节。为什么是16字节因为这个长度足够让随机空间大到没法碰撞同时对报文长度又几乎没影响。挑战值由认证方生成必须用密码学安全的随机源不能用时间戳或者自增序列凑数——我见过有实现图省事直接用秒级时间戳当挑战值结果两台设备在同一秒内发起认证挑战值撞车抓包一看两个会话的挑战值一模一样防重放的意义直接归零。另一个容易被忽略的点是挑战值是可以周期性重发的。标准允许认证方在链路建立之后的任意时刻重新发起一次挑战间隔由认证方决定常见配置是几十秒到几分钟不等。这个设计的意图是限制单次认证结果的有效期——即便某次摘要值被截获它的可用窗口也只有下一个挑战周期那么长。代价是链路上会周期性地出现认证报文且如果双方时钟或状态不同步容易出现链路莫名其妙抖动。这个特性在排查“链路每隔一段时间就断一下”的问题时是必须优先怀疑的对象。1.4 标识符字段被严重低估的匹配机关标识符只占一个字节取值范围0到255循环使用。它的作用是把“挑战”和“回应”配对起来——认证方可能同时维护多个会话收到一个回应报文得知道它是针对哪次挑战的。规则很简单回应的标识符必须和对应挑战的标识符完全一致不一致就丢弃。这个机制在正常流程里几乎不出问题但在两种场景下会咬人。第一种是重传认证方发出挑战后没等到回应超时重发重发时报文的标识符必须保持不变这样迟到的回应才能被正确匹配有些实现图省事每次重发都换新标识符结果就是客户端回应的永远匹配不上日志里表现为反复认证失败但密钥明明是对的。第二种是多链路捆绑一条物理链路上跑多个逻辑会话时标识符的分配必须隔离否则会话A的回应可能被会话B的挑战“认领”表现为随机性的认证失败极难排查。抓包时看标识符是否成对出现是判断这类问题的第一手段。2. 报文结构与摘要计算把验证落到每一个字节2.1 四种报文类型与字段布局CHAP验证在链路上跑的报文只有四类用报文头里的一个字节区分结构高度统一一个字节的类型、一个字节的标识符、两个字节的长度字段含头部自身后面跟变长的值和名字。四类报文的具体含义如下表类型值名称方向携带内容1挑战认证方到被认证方随机挑战值 认证方名字2回应被认证方到认证方计算出的摘要值 被认证方名字3成功认证方到被认证方可选的一段提示文本4失败认证方到被认证方可选的失败原因文本长度字段是个小陷阱它统计的是整个报文的字节数包括类型、标识符、长度字段本身。所以一个最短的成功报文长度是4值字段和名字字段都为空。有些实现把长度算成“值名字的长度”少了4个字节的头部导致对端解析时读越界现象是报文能收到但解析异常。这类问题在对接不同厂商设备时偶尔能碰到抓包看长度字段和实际字节数对不对得上一眼就能定位。名字字段的用途需要单独强调认证方在挑战报文里带上自己的名字被认证方在回应报文里带上自己的名字双方各自用对方报文里的名字去本地的密钥表里查对应的密钥。这意味着用户名的匹配规则不是“我配了什么就用什么”而是“对端报什么名我查什么表”。配置时客户端填的用户名和服务端密钥表里的条目名必须严格一致包括大小写。2.2 摘要值的计算顺序错一个字节就全错摘要值的计算是CHAP验证里最需要精确的一环。标准规定的输入顺序是标识符字节、密钥的字节序列、挑战值的字节序列三者直接拼接成一个连续的字节流不做任何分隔符然后整体送入摘要算法。输出的长度由算法决定常见的摘要算法输出16字节即128位。这个“直接拼接、不加分隔”的规则会带来一个理论上的歧义如果密钥的最后一个字节和挑战值的第一个字节恰好可以重新切分不同的密钥-挑战组合可能产生相同的拼接结果。标准对此的处理方式是要求密钥和挑战值都不包含某些特定字节实际实现里通常靠密钥长度和随机性来规避不用过度担心但自己写实现的时候要知道这个前提。手工验算的步骤可以这样走先把挑战报文的挑战值字段按十六进制抄下来把你配置的密钥转成字节序列把回应报文的标识符抄下来按“标识符密钥挑战值”的顺序拼成一串十六进制丢进摘要工具算一遍把结果和回应报文里的摘要值逐字节比对。这个方法我在排查“密钥明明一样为什么认证不过”的时候用过很多次十次里有六七次能当场发现密钥末尾多了一个空格或者全角字符。验算是排查认证失败最快的手段比反复改配置试错高效得多。2.3 摘要算法选择的现实处境标准最初搭配的摘要算法是MD5输出128位。这个选择放在当年是合理的计算量小嵌入式设备也跑得动输出长度够用。但今天来看MD5的抗碰撞性已经被彻底击穿理论上存在构造两个不同输入得到相同摘要的方法。需要说清楚的是抗碰撞性被击穿和CHAP验证被直接攻破不是一回事。CHAP验证依赖的是摘要算法的单向性和抗原像性即从摘要值反推输入不可行这个性质MD5目前还没有被完全摧毁。所以针对CHAP验证的实际攻击路径主要还是离线暴力穷举密钥而不是构造碰撞。但这不意味着可以高枕无忧——很多设备厂商在后来的固件里提供了更长的摘要算法选项能选长摘要就别用短的这是成本几乎为零的加固。后来的扩展版本常被称为第二版把响应长度拉到24字节引入了对端挑战值和更复杂的密钥派生还支持双向认证安全性有提升但它的密钥派生方式一直有争议。实际对接中运营商侧和部分设备默认要求用扩展版本你这边配成基础版本会直接被拒日志里能看到协商阶段的算法不匹配提示。选型时的原则是对端支持什么就用什么优先选扩展版本实在不行回落到基础版本同时把密钥强度拉满。3. 从零跑通一次CHAP验证配置、抓包、验算全流程3.1 环境准备与拓扑设计演练环境不需要复杂两台机器背靠背就够一台当认证方一台当被认证方中间用一条链路直连。如果手头没有物理设备用虚拟机加虚拟串口或者虚拟以太网点对点链路也能模拟思路是一样的。下面用一个简化的三段式拓扑说明被认证方客户端——链路——认证方服务端服务端后面挂一个地址池认证通过后给客户端分配地址。准备清单包括两台能跑开源拨号套件的机器、一条可用的点对点链路、一个提前约定好的密钥建议12位以上随机字符、以及一个抓包工具用于旁路观察。建议在演练环境里就把抓包开着因为认证流程太快等出问题了再回头抓往往抓不到现场。配置前先明确一件事谁是认证方。拨号场景里被拨入的那一端是认证方主动拨出的那一端是被认证方。这个角色划分决定了挑战报文从哪边发出也决定了密钥表配在哪边。双向认证的场景下两边都要配表各自既当认证方又当被认证方。3.2 服务端配置账号表、认证方式与挑战间隔服务端的配置分三块。第一块是账号与密钥表开源拨号套件通常用一个纯文本文件维护每行一条记录字段依次是客户端名字、服务端名字、密钥、允许分配的地址。示例格式如下# 客户端名 服务端名 密钥 允许地址 dialer01 * K7pQ2mZx9LwR4tYv 10.10.10.2 dialer02 * B3nH8sVc6FjD1gKa 10.10.10.3服务端名字写星号表示匹配任意对端报上来的名字这是最省事的写法但在多认证方共存的环境里会带来歧义严谨的做法是写死对端期望的名字。密钥字段不要用引号包裹除非密钥里包含空格或井号这类特殊字符一旦用了引号引号本身不会被当作密钥的一部分但如果你从别处复制过来带了不可见字符问题就来了。第二块是认证方式声明需要在服务端的选项配置里明确要求使用CHAP验证并拒绝对端降级到明文方式。典型写法是在选项文件里加入要求认证、指定本端名字、禁用明文认证、要求CHAP这几条指令。这里的关键是把明文方式明确禁掉否则对端可以主动发起明文认证服务端一旦接受整个防窃听的初衷就废了。第三块是挑战间隔与重试。挑战重发间隔设得太短链路繁忙时容易出现挑战和回应错配设得太长单次认证结果的有效窗口就变大。我的经验值是重试间隔3到5秒、重试次数3次挑战周期如果业务允许设成300秒以上基本不影响体验也能避免频繁认证带来的链路抖动。3.3 客户端配置用户名、密钥与降级禁止客户端这边要配的是三样拨号用的用户名、对应的密钥、以及禁止降级。用户名必须和服务器密钥表里的客户端名字字段严格一致这是最容易出错的地方——很多人习惯在客户端填“账号域”这种形式而服务端表里只写了账号部分结果服务端拿着带域的完整名字去查表查不到直接失败。密钥在客户端通常有两种存放方式写在选项文件里或者写在独立的密钥文件里。写在选项文件里直观但权限管理麻烦写在独立密钥文件里更规范文件权限建议设成600属主为运行拨号进程的用户。配置示例# 客户端选项片段 user dialer01 name dialer01 noauth # 注意仅在被认证方角色下使用含义是不要求对端认证自己 refuse-pap refuse-eap这里要专门说noauth这个参数。它的含义是“我不要求对端向我证明身份”在单向认证场景下客户端确实需要它否则客户端会反过来要求服务端认证而服务端没配对应账号协商就卡住了。但在双向认证场景下这个参数必须去掉否则等于主动放弃了验证对端的机会。我见过不少配置模板直接把这个参数抄进去结果整条链路只做了单向认证安全性打了对折还不知道。3.4 抓包验证三个报文对照着看配置完成后启动拨号同时在链路上抓包。过滤条件按协议字段来写抓出来的报文顺序应该是挑战、回应、成功或失败。逐个看第一步看挑战报文。确认挑战值字段是随机的、长度符合预期、名字字段是服务端配置的名字、标识符记下来。第二步看回应报文。确认标识符和挑战报文一致确认名字字段是客户端配置的用户名把摘要值字段抄下来。第三步做离线验算用标识符、密钥、挑战值按顺序拼接算摘要和抄下来的值比对。如果对上了但链路上仍然是失败报文那问题就不在摘要计算而在服务端的查表逻辑——最常见的是服务端用回应报文里的名字去查表而这个名字和服务端表里的条目名不一致。如果没对上问题在密钥本身或者摘要算法的选择上先确认双方用的是不是同一个算法版本。成功报文之后通常紧跟的是网络层协商地址分配、路由下发都在后面。认证成功只是拿到了入场券离业务通还差好几步这一点在第5章会展开。3.5 双向认证的配置要点双向认证的意思是双方都当一次认证方互相验证。配置上要做三件事服务端和客户端都要在各自的密钥表里加上对方的条目双方都要开启“要求对端认证”客户端侧要删掉前面提到的noauth参数。双向认证的价值在于防止“假服务端”——如果只做单向认证任何能响应链路建立请求的设备都可以冒充服务端把客户端钓上来然后套取后续的业务数据。加上反向认证之后客户端也能确认对面是不是真的服务端。代价是配置复杂度上升且两边的密钥必须互相匹配任何一边配错都会导致协商在第二次握手时失败日志上看起来像是“第一次成功了第二次失败”容易误导排查方向。提示双向认证调试时建议先把一方的“要求对端认证”关掉跑通单向之后再打开逐步定位。一次性把两边都配上出问题时你连是哪一边的密钥错了都分不清。4. 安全边界在哪CHAP验证的三个真实软肋4.1 软肋一离线暴力穷举这是CHAP验证最本质的弱点。攻击者只要能在链路上抓到一次挑战报文和对应的回应报文就可以拿回本地慢慢算猜测一个密钥用抓到的挑战值和标识符算摘要和抓到的回应值比对对上了就说明猜对了。整个过程不需要和任何服务器交互不会被限速、不会被锁定、不会留下日志。防御手段只有一个方向把密钥强度拉满。密钥的熵决定了穷举成本。假设密钥是随机选取的字符字符集62个大小写字母加数字长度和穷举空间的关系大致如下密钥长度穷举空间量级单卡每秒十亿次量级的粗略耗时6位约5.7×10^10分钟级8位约2.2×10^14小时级10位约8.4×10^17月级12位约3.2×10^21千年以上量级这张表里的数字只是量级示意实际速度取决于硬件和实现但结论很明确8位以下的随机密钥在今天基本等于没有防护12位以上的随机密钥才有实际意义。比长度更重要的是不要用有规律的密钥——字典词、键盘序列、日期、设备型号加数字这些都会被字典攻击第一时间命中长度再长也没用。4.2 软肋二密钥在服务端必须以可用形式存在这一条是运维层面最容易被忽视的。服务端验证CHAP回应时需要拿密钥原文重算摘要这意味着服务端必须能拿到密钥的明文或者某种可逆形式。这一点和后来的摘要存储方式有本质区别后者服务端只存摘要验证时算摘要比对服务端自己也不知道原文。这带来一个连锁问题当认证服务器和链路设备分离中间走集中认证协议时链路上传递的认证信息格式会影响服务端能否完成CHAP验证。集中认证协议里如果客户端用明文方式认证服务端收到的是加密后的口令可以解密后用于各种验证但如果客户端用CHAP验证服务端收到的只是识别符加摘要值它必须去用户数据库里取出密钥原文重新计算。如果你的用户数据库里存的是不可逆的摘要CHAP验证在这套架构下根本跑不起来这是很多人在迁移认证体系时撞上的墙。实际处理方式通常有两种一是给需要走CHAP验证的账号单独维护一份可逆存储的密钥隔离风险二是在链路设备上直接做本地认证把密钥表放在设备本地不走集中认证。前者便于集中管理后者更简单但规模上去之后维护成本高。4.3 软肋三摘要算法的时代局限前面提过基础版本用的摘要算法抗碰撞性已经失守。虽然实际攻击路径主要还是穷举密钥但在合规要求比较严的场景里用短摘要可能直接过不了检查。加固路径有三条优先启用扩展版本响应更长、支持双向认证如果设备支持选择更长的摘要算法实在只能用基础版本就把密钥强度和轮换周期当成主要防线。密钥轮换这一条值得单独说。很多链路的密钥从上线那天起就没换过一用三五年。轮换的难点不在技术在于协调——两端不同步就会断链。实操上的做法是先在服务端同时保留新旧两条记录客户端名字不同或者通过其他方式区分让客户端先切到新密钥确认链路正常后再删掉旧记录。整个过程可以做到不中断业务。5. 认证失败与链路异常的排查速查表5.1 认证阶段失败先看名字、再看密钥、最后看算法认证失败的排查顺序应该固定下来不要东改一下西改一下。第一顺位查名字匹配抓包看回应报文里的名字字段再去看服务端密钥表里的客户端名字字段逐字符比对包括大小写和不可见字符。第二顺位查密钥一致性用前面说的离线验算法把摘要值手工算一遍比对。第三顺位查算法版本看双方协商出来的是基础版本还是扩展版本不一致的话在配置里显式指定。常见现象和对应原因的对照如下现象大概率原因验证方法反复挑战、无回应客户端未收到挑战或解析失败抓包看挑战报文是否到达客户端有回应但立即失败密钥不一致离线验算摘要值回应报文名字与预期不符客户端用户名配置错误抓包看名字字段认证成功但很快重认证挑战周期过短或链路抖动看日志时间戳间隔第一次成功第二次失败双向认证中反向密钥错误分别检查两边密钥表5.2 认证过了业务不通问题在后面几个阶段认证成功不等于链路可用。认证通过之后还要走网络层协商地址分配、路由通告、链路参数协商。任何一步失败业务都通不了。典型表现是日志里能看到认证成功的记录但接口上没有地址或者有地址但 ping 不通对端。排查思路是分层看先确认接口上有没有拿到地址没有就查地址池配置和地址冲突有地址但不同网段查对端的路由和地址掩码配置能 ping 通网关但访问不了远端业务查路由是否下发、有没有做地址转换。还有一个隐蔽的坑是最大传输单元不匹配认证阶段不涉及大数据包所以链路看起来是通的但一传大文件就卡死。这个问题的排查方式是逐步减小 ping 包大小找到临界值再回头调整链路参数。5.3 几条花时间换来的实操心得第一条配置文件里的不可见字符是头号杀手。从聊天工具、文档、网页里复制的密钥经常带尾随空格或者全角标点。排查时不要用眼睛看用十六进制工具把密钥文件打开逐字节确认。我自己的习惯是密钥一律用脚本生成直接写入文件全程不经过手工复制。第二条日志级别要提前开。认证失败的原因在默认日志级别下往往只有一行“认证失败”什么都没有。把拨号进程的调试级别打开能看到协商的每一步和失败的具体原因。但要注意调试日志会记录密钥相关的信息排完障记得关掉别让日志文件成为新的泄露点。第三条改配置之前先备份改完一次只动一个变量。链路认证涉及两端配置同时改两边出问题根本分不清是哪边改坏的。我的做法是每次只改一端改完立刻验证验证通过再动另一端。第四条跨厂商对接时优先确认默认行为。不同实现对标准里“可选”部分的理解差异很大名字字段允不允许为空、标识符重传时是否保持不变、成功报文里的提示文本带不带、挑战值最短允许多少字节。这些细节在标准里都是“可以”在实现里就是“必须”。对接前把这些点列出来逐条确认比事后抓包猜要省太多时间。6. 关于这套认证方式还能不能用的一点个人判断CHAP验证这套机制诞生到现在几十年了报文结构没变过核心流程也没变过。它今天的定位很清楚在链路层做一个轻量的、不传明文口令的身份确认。它不是加密协议不是访问控制也不是审计手段把不该它承担的责任压上去早晚要出事。我在实际使用中的体会是CHAP验证的成败几乎从来不由算法决定而是由密钥管理决定。同一套配置密钥用设备型号加序号和用脚本生成的12位随机串安全性差着好几个数量级密钥三年不换和按季度轮换暴露窗口也完全不同。真正需要投入精力的地方是密钥的生成、分发、存储、轮换这条链路而不是在配置里纠结用哪个版本的摘要。还有一点如果你的场景对安全性的要求已经超出了“防止口令明文传输”这个层级那CHAP验证就不该是主防线了它只能作为链路建立的第一道门槛后面还得叠加隧道加密、访问控制列表、流量审计这些手段。单靠一个认证协议撑起整条链路的安全这个思路本身就是有问题的。配置的时候多想一步“万一这一层被绕过了后面还有什么”比反复加固这一层更有价值。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑