资讯动态

Web身份验证攻防实战:枚举、爆破与2FA绕过技巧

发布时间:2026/10/22 22:07:18 来源:尧图企业网站定制
1. 项目概述与攻击面拆解1.1 这个实战笔记想解决什么问题最近我在梳理 Web 身份验证相关的攻防思路时把 PortSwigger 实验室里关于认证机制的那部分案例从头到尾重新刷了一遍。从最基础的用户名枚举开始到密码爆破、会话令牌分析再到 2FA 绕过和通过宏Macro实现自动化爆破整个链路走下来最大的感受是身份验证不是单点问题而是一条可以被逐层拆解的信任链。这条笔记适合三类人。第一类是刚接触 Web 安全、想做渗透测试入门但不知道怎么下手的新手第二类是已经会抓包改包、但总觉得思路不成体系的初中级测试人员第三类是在做蓝队防御、想搞清楚攻击者会从哪里突破身份验证的开发者或安全运维。不管你是哪一类这篇笔记里都不会出现悬空的理论所有内容都能落回到 Burp Suite 的实际操作上。这套内容能解决什么问题说白了就是三件事怎么更快地发现认证机制的薄弱点怎么在不需要复杂工具的情况下完成高成功率验证怎么把手工测试中重复性的劳动自动化。我在刷这些案例的过程中踩了不少坑比如刷着刷着发现会话失效了、宏配置错了导致请求链路断裂、枚举时忽略了响应长度的微小差异等等。这些坑在官方文档里往往只有一句话带过但实际操作时真的会卡人一两个小时。所以我想把这些经验和细节沉淀下来。这篇文章里写的所有攻击手法都只适用于你有授权的测试环境比如 PortSwigger 自带的靶场、自己搭建的练习系统或者签了测试授权的目标系统。未经授权的探测和爆破在任何国家和地区都不是小事别把练习环境里的技能带到生产环境去乱试。1.2 认证机制的常见战场与测试路径身份验证听起来范围很大但真正需要我们关心的核心节点其实就这几个登录入口本身用户名/密码的提交逻辑包括参数格式、加密方式、请求频率限制。会话生命周期登录成功后服务端发放的 Session ID、Token它们怎么生成、怎么校验、怎么失效。多因素认证除了密码之外的验证层常见的有短信验证码、邮件验证码、TOTP 动态口令。密码找回与账户恢复这类辅助流程往往更容易被忽视也经常存在逻辑漏洞。登录态下的敏感操作复核比如修改密码、绑定手机号时是否需要重新验证身份。PortSwigger 的认证实验室覆盖了上面几乎所有方面。我自己的测试路径一般是这样先用枚举确认目标系统里存在哪些有效用户名再针对这些用户名尝试密码爆破同时观察系统是否有时序锁定、IP 锁定、CSRF Token 刷新等防御机制。如果爆破被卡住就转向分析会话管理或者看 2FA 的验证逻辑是否可以被绕过。这条路径看起来简单但每一步都有细节。比如很多人一上来就直接挂字典跑爆破结果跑了半天发现用户名全是无效的白费力气。又比如有些人明明拿到了一个有效会话却在测试 2FA 时把会话搞丢了导致后面所有验证都失败。下面我会按这条路径把每个环节怎么做、为什么这么做、有哪些坑逐一展开。2. 先从枚举探测开始把立足点找到2.1 为什么要先枚举用户名而不是直接爆破很多初学者有个误区觉得爆破就是拿一个大字典对着登录接口猛跑。但实际上如果连有效用户名都不知道爆破的效率和准确率都会大打折扣。从攻击者的角度看用户名枚举的价值在于缩小爆破范围。一个典型的业务系统里用户名往往是邮箱或者手机号它们的格式规则相对固定。如果你能确认一批有效用户名后面爆破密码时不需要在字典里混入“无效用户名”干扰结果每条请求的有效性都会更高。从测试方法论的角度看枚举本身也是在探路。你通过枚举能发现系统在身份验证上存在的问题——比如错误提示过于精确、响应内容可区分、时序存在差异等等。这些信息本身就是漏洞证据可以在测试报告里作为中低危问题提交也可以作为进一步攻击的前置条件。在实际操作中我一般会同时使用两种策略一种是用小字典快速探测常见用户名比如 admin、test、root、info、support 这类另一种是结合目标系统的业务场景去收集真实的用户标识。PortSwigger 实验室里通常会直接给出一些线索比如注册页面返回的提示语、已经暴露的用户邮箱前缀等。要把这些信息利用起来而不是死板地跑字典。2.2 响应差异分析从状态码到响应体用户名枚举最常见的判断依据就是系统对“用户名不存在”和“密码错误”这两种情况给出的响应不同。这个差异可能出现在三个层面。第一是状态码。某些系统会用 200 表示登录失败但用 404 甚至 302 表示资源不存在或者反过来。理论上正规系统应该统一返回 401 或者 200但很多老旧系统、外包项目就是不规范。PortSwigger 的一些入门级实验室就故意设计了这种问题。测试时先手工提交两个不同的请求对比状态码差异如果有差异直接就能确认枚举漏洞。第二是响应体的内容和长度。比如用户名不存在时提示“Invalid username”密码错误时提示“Incorrect password”这属于最直白的差异。还有一些系统虽然用了相同的提示文案但响应长度不一样——多了一个空格、多了个小写字母或者 HTML 注释肉眼看不出来但 Intruder 的对比功能会很明显地展示出来。第三是响应头或行为差异。例如某些系统在用户名不存在时不会设置 Cookie而在密码错误时设置了 Cookie或者用户名不存在时请求时间明显缩短因为系统直接短路了查询逻辑。在测试时把响应包完整展开看不要只盯返回体Header 里的 Set-Cookie、Content-Type、Server 信息有时候都会泄露线索。我习惯在 Burp Suite 的 Intruder 里把匹配项同时设置为状态码、响应长度和关键字一次性做对比。如果某个用户名对应的响应长度和其他样本有明显差异就标记出来手动复核。2.3 利用错误消息、时间差异与登录响应细节除了上面提到的直观差异还有一些更隐蔽的枚举技巧。时序攻击是很有意思的一种。某些系统在用户名不存在时会先查用户名表没查到就直接返回而在用户名存在时会继续查密码哈希、做比对耗时明显更长。如果这个时间差足够稳定且可测量即使响应内容完全相同也能通过 Intruder 里的响应时间列判断哪些用户名是有效的。有个实验室的案例就是这样密码哈希算法相对复杂存在用户时响应时间会慢几百毫秒用响应时间排序后结果一目了然。登录响应细节也值得深挖。比如第一次用不存在的用户名登录服务器返回的某个 Cookie 值保持不变换成存在的用户名后Cookie 值会变长或者追加了一段新内容——这往往说明后端逻辑走了不同的分支。还有一种情况是系统会返回“密码错误”和“账户已锁定”两种提示前者意味着用户名有效且未锁定后者说明账号存在但被保护了。这些都是枚举阶段可以利用的分支信号。实际操作时我会先把同一请求重放两三次确认响应差异不是随机的。因为有些应用会在响应头里注入随机值或者负载均衡导致多个后端响应不一致。只有当差异稳定可复现时才能作为判断依据。提示PortSwigger 的实验室里枚举部分通常不会设置请求频率限制但真实系统里会。所以我在做枚举时都会控制线程数先用单个线程确认规则再逐步提高并发避免触发封禁导致结果失真。3. 登录爆破中的细节博弈3.1 请求结构分析与令牌处理拿到有效用户名之后下一步是尝试密码爆破。但这一步远不是把字典灌进 Intruder 那么简单。爆破的第一步是搞清楚登录请求的完整结构。先看请求行的路径和 HTTP 方法。有些系统登录入口是 POST /login有些则是 POST /api/auth还有的是用 XMLHttpRequest 异步提交。请求路径不同对应的防护策略也可能不同——比如后端可能只对页面登录接口做了频率限制但 API 接口没做任何防护。这往往是爆破突破口。再看请求体里的参数。一个典型的登录请求长这样POST /login HTTP/1.1 Host: target.example.com Content-Type: application/json {username:admin,password:123456}注意 Content-Type 是 JSON 还是表单格式。如果是 JSONIntruder 里对参数做爆破时需要选择合适的位置标记并且要注意转义问题。如果是表单格式CSRF Token 通常也是请求体的一部分。Burp 的 Intruder 有两种处理方式用宏自动刷新 Token或者用手工提取 Token 后单独标记为不参与爆破的资源。后面我会详细说宏的配置。还要关注 Cookie。很多系统在登录页面会预置一个 session 或 csrfToken 的 Cookie爆破时如果 Cookie 过期或需要更新要继续使用旧的 Cookie 会导致请求全部失败。这种问题我在测试时遇到过不止一次后面会在排查章节展开。3.2 绕过频率限制的几种思路真实系统里爆破最大的拦路虎通常是频率限制和账户锁定。理解这些限制的绕过思路对测试深度有很大帮助。第一种思路是IP 轮换。限制通常基于 IP 维度比如 10 次失败后锁定该 IP 15 分钟。如果你能控制代理池或者使用 IPv6 地址池就能轻松绕过这种限制。但这个方法依赖基础设施不是所有测试场景都适用。第二种思路是分布式爆破。不是换 IP而是换入口——同一套凭据在登录接口失败一定次数后转去密码重置接口试试或者去移动端 API 接口试试。很多系统的频率限制只做了 Web 端API 端完全裸奔。这种思路要求你先做接口资产梳理把同一个认证功能的不同入口都找出来。第三种思路是请求混淆。添加 X-Forwarded-For 头部、X-Real-IP 头部值为任意 IP有些系统真的会信任这些头部而绕过频率限制。虽然这是很老套的手法但我在不少真实项目里仍然见到过。第四种思路是利用时间窗口。有些系统的锁定逻辑是“连续失败 N 次锁定 M 分钟”但如果你在每个锁定周期内只试 N-1 次隔一段时间再试就能把爆破周期拉长用时间换空间。这种方式慢但胜在稳妥。PortSwigger 的 2FA 宏爆破实验室里也涉及到了类似思路的不同变体。注意这里说的所有绕过思路都只是为了让你在有授权的测试中验证防御有效性。别拿这些方法去对付你不拥有的系统。3.3 用户名密码字典的精细化构造爆破的效果很大程度上取决于字典质量。PortSwigger 的实验室里通常会给出一个候选密码列表但真实项目中不可能这么轻松所以我会结合目标情况手动构造字典。首先从目标系统收集信息。注册页面的密码规则会告诉你最小长度、是否要求特殊字符找回密码页面的提示语可能泄露密码策略的细节。这些信息能帮你过滤掉字典里大量不合规的词条让每个密码尝试都更接近真实。其次基于目标语言习惯定制。如果是国内系统用拼音名字加生日、公司名加年份这类组合命中率很高如果是英文系统天气、球队、宠物名这类词汇命中率也比较可观。把弱密码字典和这些自定义规则合并能明显提高爆破成功率。最后要注意大小写和变形。很多系统要求密码包含大小写字母所以爆破时不能只试纯小写。我一般在 Intruder 里同时使用字典中原词、首字母大写、全大写、末尾加数字等变体。这些变体会成倍增加请求数量但对应的是真实场景里的用户习惯。有了好的字典爆破的命中率才会上去。否则即使绕过了一切限制纯靠暴力碰撞的效率也低得可怜。4. 2FA 绕过与宏爆破4.1 2FA 机制中容易被忽视的逻辑缝隙多因素认证在很多人眼里是“不可攻破”的但实际测试里我见过太多 2FA 的落地缺陷。PortSwigger 最经典的一个实验室核心漏洞就是2FA 校验与会话状态脱节。具体来说正常的 2FA 流程是用户提交用户名密码后系统设置一个未完全验证的会话 Cookie同时发送验证码到用户手机或邮箱。用户提交验证码后系统校验通过把会话标记为已验证。这个流程本身没问题但某些实现里存在两种情况可以绕过。第一种情况验证码不是与账号绑定的。系统的验证码校验接口只校验验证码本身是否正确而不校验这个验证码是发给谁的。攻击者可以用自己的账号接收验证码然后把这个验证码用在受害者的账号上。这种漏洞在短信验证码的电商类系统里并不少见。第二种情况验证步骤可以通过修改请求跳过。比如登录后跳转到 2FA 页面系统通过请求中的某个参数判断用户是否已通过 2FA比如verify: false、step: 2fa。如果直接把这个参数改成verify: true或者访问一个未校验权限的后续接口就能绕过验证步骤。PortSwigger 的某个实验室就是利用第二种思路。在登录接口成功之后把请求路径从/login改成直接访问/my-account发现系统没有强制跳转到 2FA 页面而是直接返回了账户信息。原因很简单后端只在执行 2FA 流程的接口上做了校验但没有在资源访问的必经链路上统一校验会话的“已验证”状态。这个案例给我最大的启发是2FA 不是一个“功能”而是一个“状态”。如果这个状态没有渗透到所有敏感操作里那它就只是摆在那里的一层装饰。4.2 2FA 验证码爆破的痛点和宏的引入当 2FA 逻辑本身没有明显的绕过漏洞时另一个思路是直接爆破验证码。这个思路成立的唯一前提是验证码位数足够短常见 4 位或 6 位数字且系统没有严格的尝试次数限制。4 位数字只有 10000 种组合6 位数字也只有 100 万种听起来好像不少但对于自动化工具来说完全可控。问题是爆破过程中需要处理的往往不是那 10000 次请求本身而是每 次 请 求 都 要 带 上 一 个 新 的 CSRF Token 或 新 的 会 话 Cookie。有些系统每刷新一次验证码页面就会废弃旧的 Token并且把旧的会话标记为无效。如果只是手工抓一个 Token 然后循环替换验证码字段第二次请求就会失败因为 Token 已过期。必须做到“每次爆破尝试前自动获取一个新的 Token并携带这个新 Token 发起验证码提交请求”。这就是宏技术的用武之地。宏Macro是 Burp Suite 里一项非常实用的功能它能让你在每次请求前自动执行一组预设的子请求从子请求的响应里提取动态值再把提取到的值注入到后续的主请求中。这样就能在 Intruder 跑爆破的同时维持会话的新鲜度。4.3 宏配置实操从零到一发入魂我以 6 位数字验证码的爆破为例完整演示一遍宏的配置过程。第一步录制宏。在 Burp Suite 的 Project options - Sessions - Macros 里点击 Add然后选择一个 GET /2fa 页面这样的请求作为宏的子请求。Burp 会记录这个请求的完整链路包括响应包。第二步提取动态值。在宏录制完成后配置一个 Cookie Jar 更新规则。关键是告诉 Burp从宏的响应中提取 Set-Cookie 里的某个 Cookie 值更新到全局 Cookie Jar 中。同时如果验证码页面 HTML 里有隐藏的 CSRF Token需要在宏的响应里添加一个提取规则把 Token 值提取出来。第三步创建 Session Handling 规则。在 Session Handling Rules 里新建规则作用域限定在爆破验证码的这个 Intruder 请求上。规则的动作选择“Run a Macro”选中刚才录制好的宏然后在参数更新设置里把提取出来的 Token 值替换到主请求的csrf参数位置。第四步配置 Intruder。把验证码参数标记为爆破位置Payload 类型选择 Numbers设置为从 000000 到 999999步长 1。线程数不要太高因为每次都跑一次宏如果线程太高宏的执行顺序会混乱导致 Token 和 Cookie 对不上号。我个人习惯用单线程或双线程跑这种爆破速度虽然慢一点但准确率高。整个流程跑起来后你可以在 Intruder 的结果列表里看到每次请求前 Burp 会自动执行宏把新的 Token 和 Cookie 注入到请求里。当某个请求返回的状态码或响应长度明显异于其他样本时那条往往就是验证码命中后的成功响应。提示宏配置中最容易出错的地方是步骤顺序。宏必须先刷新页面获取 Token然后拿这个 Token 才能提交验证码。如果顺序反了Token 就是上一次的注定失败。配置完宏后一定要先在 Repeater 里手动触发一次宏验证 Token 提取和注入是否正常再启动 Intruder。4.4 宏和 Intruder 配合的进阶技巧上面的流程是基础版实际测试中你会遇到更多复杂情况。这里分享几个我实测有效的小技巧。技巧一多级宏。有些系统的 2FA 页面加载时不止一个请求而是先请求页面框架再异步加载 Token。如果单纯录制一个 GET 请求不够可以把整个请求链录制成一个宏——Burp 允许在一个宏里包含多个请求第一个请求的响应里提取到的值可以作为第二个请求的参数。这种多级宏在面对 SPA单页应用时几乎是必须的。技巧二响应条件判断。Burp 的 Session Handling Rule 可以对宏执行后的结果做条件判断。比如宏执行后检查响应值是否发生了变化如果发现新的 Cookie 值没有被更新就认为整个会话已失效直接终止爆破。这在应对会话随机失效的系统时非常有用可以避免大量无效请求浪费时间。技巧三验证码长度判断。4 位和 6 位验证码的爆破时间完全不同。4 位验证码从 0000 到 9999单线程最多 1 万次请求几乎瞬间完成6 位验证码单线程 100 万次请求就要跑很久。这时候需要考虑用分布式或者先缩小范围比如确认验证码是纯数字还是包含字母再做爆破。PortSwigger 实验室里常用 4 位数字验证码那是教育环境的特殊性真实系统里 6 位数字加次数限制已经很普遍了。这些技巧不是炫技而是从实际踩坑中总结出来的。宏这个功能在遇到动态 Token、动态 Cookie、多请求链路的场景时能把原本只能手工完成的重复劳动解放出来非常值得投入时间熟练掌握。5. 常见问题与排查技巧实录5.1 爆破过程中的典型故障与排查思路理论讲完了接下来是实操中大家最容易翻车的环节。我把这一路上撞过的坑按出现频率排序整理成速查表。现象可能原因排查思路所有爆破请求返回 401/403CSRF Token 未更新或格式错误检查宏是否提取到了正确的 Token 字段对比原始请求中的参数名和位置前几次请求成功之后全部失败会话被服务器主动失效或 Cookie 未同步更新检查 Cookie Jar 是否开启查看宏是否每次都会更新 Cookie必要时在宏里强制刷新会话爆破结果里响应长度全部相同验证码在服务端被静默丢弃或请求体格式不对用 Repeater 手工重放一次成功的请求确认请求格式完全一致后再跑 Intruder宏执行超时或链路中断录制宏时包含了非必需请求导致单次执行耗时太长精简宏的请求链只保留真正承载动态值的请求去掉静态资源加载请求Token 提取规则匹配到多个值页面里有隐藏 Token 和可见 Token 同时存在细化提取规则使用正则匹配具体字段属性把提取范围限定到表单内的 Token爆破速度太慢每次请求都会执行一次宏宏本身有开销适当调高线程数同时注意宏执行顺序的稳定性可用单线程先跑少量样本确认准确率5.2 容易被忽略的错误配置细节除了上面表格里列的硬性问题还有不少隐蔽细节会导致结论误判。响应长度排序的陷阱。在爆破验证码时如果系统对每个错误验证码返回的内容长度都一样只有正确验证码返回不同长度那排序法很有效。但只要系统在响应头里加入了时间戳、随机数或者动态广告内容长度排序就会产生很多噪声。这种情况要改用关键字匹配把登录成功页面里的特征字符串作为匹配项。Cookie 的作用域问题。Burp 的 Cookie Jar 是按域名维护 Cookie 的。如果主请求和宏的子请求域名不同比如一个走www.example.com另一个走api.example.comCookie Jar 不会自动共享。需要在 Cookie Jar 规则里把两个域名都加上或者改用自定义参数提取的方式手动传递 Cookie 值。爆破完没有二次验证。当 Intruder 结果显示某条请求与众不同时不要急着下结论。把这条请求放到 Repeater 里重放确认是否真的拿到了已认证的会话再把该会话对应的 Cookie 放进浏览器里模拟登录后的操作确认能访问到账户数据。只有完成了二次验证才能确认验证码爆破成功。我有一次就是因为直接看响应长度差异就下结论结果后来发现那个响应只是系统的一个通用报错页长度恰好相同。宏失败的灰度处理。如果宏在爆破过程中偶发失败Intruder 会直接跳过该次请求导致部分组合没有被测试到。所以在暴破结束后要对 Intruder 的状态列做检查必要时补跑失败区间的组合。这个问题在设置过低的超时时间时尤其容易发生。5.3 关于枚举与爆破的防御视角聊了这么多攻击手法最后我想换到蓝队视角聊聊怎么防。身份验证的防御不是单纯加一个验证码或者限制 IP 就能解决的。从我测试的经验来看真正有效的防御体系至少要包含统一错误消息不区分“用户名不存在”和“密码错误”在服务端做请求频率限制不信任任何客户端传来的 IP 头对验证码等动态凭据设置严格的尝试次数和有效时间窗口为敏感操作强制要求输入当前密码防止会话劫持后的越权操作在关键路径上统一检验会话的 2FA 状态而不是只在某个页面做校验。这些措施的每一条都对应着上面分析和测试过的攻击手段。安全测试的本质不是找漏洞本身而是理解漏洞背后的设计缺陷并把这种理解用于反向加固。这是我在刷完 PortSwigger 身份验证系列之后最大的感触。6. 个人实操心得与后续扩展这个系列刷下来我自己最大的变化是不再把“爆破”当成一个无脑撞库的动作而是当成一个需要设计的流程。设计的前提是理解目标系统怎么区分“你是谁”和“你输入了什么”——这两者之间存在的每个环节都可能是一个值得测试的点。如果后续想继续深挖我会重点看两个方向。一是 OAuth 2.0 和 JWT 这类现代认证协议中的逻辑漏洞它们比传统表单认证更复杂也更难防御二是服务端模板注入与身份验证结合的利用链比如一旦拿到了模板注入点身份验证的绕过往往就变得微不足道。你可以把 PortSwigger 的认证专题当成一个入口刷完之后顺着他推荐的进阶专题继续往前走方向基本不会错。最后再分享一个小技巧每次完成一个实验室不要急着关掉 Burp 就完事。把当时的 Intruder 配置、宏定义和整个过程截图保存下来整理成一份自己的笔记。Repeater 里那些失败尝试的请求也别删回头翻看的时候真的能帮你少走很多弯路。我自己整理这些素材时经常能发现当时忽略了但后来很有用的信息。

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

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

免费获取报价 →
↑