1. 先说清楚:这个模块到底在演练什么暴力破解,很多刚接触安全测试的朋友会觉得"这不就是拿个字典一直撞吗",但真到自己上手的时候,往往会卡在三个地方:第一不知道爆破目标是什么,第二不知道爆破结果怎么判断,第三遇到验证码、token这类防护就直接懵了。Pikachu靶场里的暴力破解模块,恰恰就是针对这些问题设计的,它把"爆破"这件事拆成了四个由浅入深的场景:基于表单的简单爆破、基于客户端的验证码绕过、基于服务端的验证码绕过,以及带token防爆破机制的爆破。我个人非常推荐刚入门的朋友把这个模块完整走一遍,不是因为爆破本身多高级,而是它能把Burp Suite的Intruder、抓包改包、会话处理这些基本功一次性串起来。往深了说,这个模块练的其实不是"破密码"这个动作,而是怎么分析一个登录入口的防护逻辑——先看它防了什么,再找它没防住的地方。这个思路在后面的SQL注入、越权、逻辑漏洞里全部用得上。这篇内容我会按实战的顺序来写,从环境准备到每个场景的具体操作路径,包括抓包、设置爆破参数、判断结果、以及我在实际练习中踩过的一些坑。如果你已经会基本的Burp抓包,可以直接跳到第三个场景开始看;如果刚接触,建议从第一个场景一步步走,因为后面很多操作是叠加上去的。2. 动手前的准备工作:环境、工具、字典2.1 把靶场跑起来Pikachu靶场通常是基于PHP环境的本地Web应用,很多学习者在Windows上用集成环境(比如小皮面板这类)直接部署,也可以用Docker拉一个现成的镜像跑起来。我个人更推荐后者,因为清理起来干净,出了问题重新起一个容器就行,不会把本机环境搞得乱七八糟。启动之后确认三个东西能访问:靶场首页、靶场的数据库初始化页面、以及目标漏洞页面本身。很多第一次练爆破的读者会遇到一个问题:页面能开,但是点提交之后数据库报错,这就说明靶场依赖的数据库没初始化,去执行一次初始化脚本就好了。浏览器这边,要把代理指向本机Burp监听的端口,同时关掉浏览器的自动填充密码功能,后面抓包会干净很多。这个细节别忽略,自动填充经常会往请求里带出一些额外的参数,干扰你对请求结构的判断。2.2 Burp Suite的配置要点爆破依赖的核心功能是Intruder,使用前建议先确认一下Burp Suite的版本和加载方式。社区版也能用Intruder,只是爆破速度有限制,对靶场这种低难度场景其实完全够用,不需要一上来就考虑专业版。需要提前设置的选项有两个:一是User options里的Connections,把超时时间稍微调大一点,防止靶机响应慢导致连接被判断为异常;二是Intruder的Resource pool,建议把并发线程控制在5以内。可能有人会觉得线程开多一点更快,但实际上靶场本地环境扛不住高并发,线程开大了反而会出现响应超时、数据错乱,结果反而不准。2.3 字典不用贪大,但要有结构爆破效果很大程度取决于密码字典。直接去下载一个几千万条的超大字典,在靶场这种简单场景里其实是浪费。建议准备一个200到500条左右的高频弱口令字典,里面包含常见密码、拼音组合、键盘序列、带年份的数字组合就够了。我一般会按这个思路组织字典:先放纯数字组合(123456、123456789、888888这类),再放拼音组合(admin、password、123qwe),然后放键盘序列(qwerty、1qaz2wsx),最后放带简单变化的密码(admin123、admin888、test123)。用户名通常就是admin、test这类固定值,不需要做用户名枚举,靶场这个模块本身也不考那个点。3. 最基础的那种爆破:基于表单的暴力破解3.1 抓包,先看清请求长什么样打开基于表单的暴力破解页面,随便填一组用户名密码提交。回到Burp的HTTP history里,找到对应的POST请求。这里要注意,如果页面有前端校验逻辑,比如空密码提示,那得先在Burp里把浏览器拦截关掉或者直接绕过前端校验再抓包,不然看不到真实请求。请求里最关心的就是POST body中的参数,比如username=admin&password=123456。这决定了爆破时payload要打的位置。很多初学者一上来就把整个请求丢进Intruder,然后发现结果全是同一个响应长度,根本分不清哪次是成功的,原因就是没在payload position里标对位置。3.2 Intruder的两种模式怎么选Intruder里有多个attack type,这个场景用Sniper和Pitchfork比较常见:Sniper模式:一次只爆破一个位置,适合固定用户名、爆破密码的场景。把密码参数的值选中,点击Add标记为payload position,用户名保持固定值。Pitchfork模式:可以同时爆破多个位置,每个位置对应一个字典。适合既不知道用户名也不知道密码的场景,但需要准备两个字典文件,并且两个payload set要一一对应——它按行配对,不是做笛卡尔积。3.3 怎么判断爆破成功了爆破结果不能只看状态码,靶场环境登录失败通常也返回200,所以要从两个维度判断:响应长度和响应内容。打开Intruder的Attack窗口,跑完之后按Response length排序,长度明显不同的那条就要重点看。如果还是不确定,直接点开该条记录的Response,搜一下有没有"success"、"登录成功"这类关键词。这里的坑是:有的靶场版本失败时也会返回200并且带一些提示文字,长度差异可能不大,那就需要在Grep-Match里提前配置关键词,比如配置"success"和"failed",跑完之后一眼就能看到哪条命中。这一步是整个爆破练习中最容易让人怀疑人生的环节,希望大家多花点时间理解响应差异判断的思路,这比爆破本身更有价值。4. 验证码不是绝对防护:两种绕过思路的完整复盘4.1 先理解验证码为什么会失效很多初学者把验证码当成"一定安全"的东西,其实验证码在Web应用里能不能起到防护作用,完全取决于服务端的校验逻辑怎么写。Pikachu靶场的验证码模块设计了两类典型缺陷,正好对应两种完全不同的绕过方式。第一类问题出在客户端:验证码的校验逻辑写在了前端JavaScript里,服务端根本不校验验证码。这种情况你直接看前端代码就能发现,验证码只是"看起来在验证"。第二类问题出在会话管理:服务端确实校验验证码,但验证码生成之后没有失效机制,同一个验证码在会话有效期内可以反复使用,你只要在爆破前手动填一次正确的验证码,然后保持这个会话去爆破就行。4.2 基于客户端的验证码绕过:直接爆破,不用管验证码打开对应页面,按F12看页面源码,搜索"verify"或者"check"这类关键字,你会发现验证码的校验函数在本地。这意味着验证码的判定结果完全由前端控制,攻击者只需要绕过前端脚本,直接把请求发给服务端,验证码字段随便填都不会被检查。实操路径是这样的:先正常抓一次提交请求,把请求体里验证码的参数单独标记出来,然后在Intruder里把验证码参数设为固定值,比如填个"1",同时把密码参数设为payload position。也就是说,验证码在这个请求里就是一个没用的字段,服务端根本没有取它的值去校验。4.3 基于客户端的验证码绕过:我要不要专门写一段Python脚本模拟请求当然可以,而且我建议你试一次。用Python脚本模拟整个爆破流程,本质上就是通过脚本发送HTTP请求,绕开浏览器的前端校验,把用户名、密码、验证码字段循环替换。脚本逻辑非常简单,一个for循环加一个requests.post就可以搞定。写脚本的好处是你能更直观地看到"验证码根本没进服务端校验逻辑"这个过程。4.4 基于服务端的验证码绕过:核心是"不刷新"服务端校验验证码的场景会稍微复杂一点,但突破点更典型。靶场这个场景里,验证码在服务端会校验,但不会在每次校验后失效——也就是说你第一次请求时填了一个正确的验证码,后续所有请求只要带着这个验证码,服务端都会认为正确。操作上分三步:先正常打开页面获取验证码图片,手动识别一次,填进表单;然后抓包,把验证码这个参数的值固定为刚才识别出来的结果;最后用Intruder对密码进行爆破。这条链路能跑通的核心原因是:验证码只绑定会话,不绑定使用次数,也没有过期机制。4.5 两类缺陷的对照:为什么说验证码设计是个"系统工程"把两类场景放在一起看,会发现它们是从不同层面失效的。前端的验证码失效,本质上是因为逻辑放错了层——校验放在客户端等于没有防护;后端的验证码失效,是因为校验逻辑缺少状态管理——生成了验证码、校验了验证码,但没把"已使用"的状态记录下来。真正合格的验证码防护应该满足三个条件:服务端生成、服务端校验,验证码使用后立即作废,验证码与用户会话、时间窗口绑定。靶场这个模块的价值就在于:它把一个本来应该做到位的方案拆成了缺陷案例,让你从攻击视角理解防御逻辑的每一个组成部分。5. Token防爆破:带动态参数的请求还能不能打5.1 Token机制的原理与真实难度Token防爆破是靶场暴力破解模块里最有含金量的一关。它的思路是:每次加载登录页面时,服务端生成一个随机token,嵌入到表单的隐藏字段里。提交登录请求时,客户端必须把这个token带回服务端,服务端校验token合法才会继续处理用户名密码。这个机制打掉了最朴素的爆破方式:因为每次请求的token都是新的,你如果拿一个固定的旧请求去重放,服务端一查token不匹配就直接拒绝。单次请求单独看,攻击者面临的是一个"每次都要从页面取新token再提交"的问题。5.2 手工爆破的思路转变面对token防爆破,常规的Intruder直接爆就行不通了,需要转变思路:能不能在每次爆破请求前,先去访问一次登录页面,把新鲜的token取出来,然后把它填到提交请求里?我最早尝试的方案是用Burp的宏功能,也就是Session Handling Rules。配置的大致逻辑是:在Intruder发起每个请求之前,先触发一个预先录制好的GET请求,从登录页面的响应中提取token参数,替换到当前请求的token字段里。这样一来每次爆破用的token都是最新获取的,等同于模拟了一个正常的浏览器行为。配置过程中最容易踩的坑是正则表达式提取。Burp里用正则提取token值时,需要准确匹配隐藏字段的格式,比如name="token" value="xxxxx",写正则时务必考虑双引号转义。我第一遍测试时,正则没匹配到值,导致每次请求的token都是空的,结果所有响应全是失败。排查了半天才发现是正则表达式里没有处理value后面的空白符。5.3 用Python脚本爆破token场景如果觉得Burp的宏配置不好调试,也可以直接写Python脚本。逻辑和上面一样,只不过由代码来管理"取token—提交请求"的过程:import requests import re session = requests.Session() base_url = "http://192.168.x.x/pikachu/" for pwd in ["123456", "admin888", "password"]: resp = session.get(base_url + "login_page") token_match = re.search(r'name="token" value="([^"]+)"', resp.text) token = token_match.group(1) data = { "username": "admin", "password": pwd, "token": token, } result = session.post(base_url + "login_submit", data=data) if "success" in result.text: print("成功密码:", pwd) break这段脚本不复杂,但它把token防爆破的本质表达得非常清楚:你只需要保证每次提交前拿一次新token,爆破就从"不可能"变成了"慢一点但可行"。5.4 为什么token防爆破依然可以被绕过从靶场这个场景能看出,token防爆破的强度取决于两个关键点:token是否绑定用户会话,以及token是否一次性使用。如果做到了这两点,爆破基本可以放弃;但如果只做到了"页面里有token",却没把token和具体会话绑定、或者token更新不够及时,那么用宏或者脚本就能轻松绕过。练习这个场景时,我还会建议大家顺手试一下:如果token生成规则有规律可循,比如是时间戳加固定位数,那么也可以尝试预测token,而不需要每次去页面获取。不要认为这是偶然情况,实际系统里token生成算法存在弱熵的情况并不罕见。6. 从进攻视角回到防守视角:爆破练习带来的几个防御启示6.1 爆破的本质是"枚举可能取值"暴力破解的本质是用枚举替代穷举:攻击者不知道正确的口令,但可以遍历候选口令空间。防御者的思路因此非常清晰:要么让枚举的代价高到不划算,要么让每次枚举的结果都不可复用。我在做完靶场练习后,对验证码设计的看法发生了很大变化。以前总觉得验证码影响用户体验,能不加就不加;练习之后发现验证码不是可有可无的装饰,而是防止自动化攻击的关键屏障。真正设计合理的验证码,必须做到服务端校验、使用后作废、有效时间短、与用户会话绑定。少任何一个条件,验证码的防线都会被击穿。6.2 登录接口是暴力破解的集中攻击面所有涉及口令验证的接口,都应该默认存在被爆破的风险。防御上常见的措施包括登录失败次数锁定、IP维度限速、账号维度锁定、以及双因素认证。我做爆破练习时,为了模拟真实情况,会在本地写一个简单的登录接口测试这些策略,这比单纯在靶场上爆破更有意思。需要注意的坑是:锁定策略如果只锁定账号不锁定IP,攻击者可以换着IP继续撞;只锁定IP不锁定账号,也没法阻挡分布式来源的攻击。两种策略需要配合。6.3 爆破能力对防御者依然关键最后说一点个人体会:很多人学了爆破之后觉得"不过如此",但真到做防御评估的时候,爆破测试反而是最容易找出系统弱点的入口之一。你不需要真的攻破什么,只需要证明登录接口没有做防枚举限制,就足以写进一份有分量的安全报告中。从靶场出来后,我建议大家找一个自己写的或测试用的Web应用,把同样的流程(未加防护版本、加验证码版本、加token版本)各走一遍,亲手对比一下攻击成本的变化。这种体感上的差异,比任何漏洞描述都更能让一个开发人员记住"为什么需要防护"。
1. 先说清楚:这个模块到底在演练什么暴力破解,很多刚接触安全测试的朋友会觉得"这不就是拿个字典一直撞吗",但真到自己上手的时候,往往会卡在三个地方:第一不知道爆破目标是什么,第二不知道爆破结果怎么判断,第三遇到验证码、token这类防护就直接懵了。Pikachu靶场里的暴力破解模块,恰恰就是针对这些问题设计的,它把"爆破"这件事拆成了四个由浅入深的场景:基于表单的简单爆破、基于客户端的验证码绕过、基于服务端的验证码绕过,以及带token防爆破机制的爆破。我个人非常推荐刚入门的朋友把这个模块完整走一遍,不是因为爆破本身多高级,而是它能把Burp Suite的Intruder、抓包改包、会话处理这些基本功一次性串起来。往深了说,这个模块练的其实不是"破密码"这个动作,而是怎么分析一个登录入口的防护逻辑——先看它防了什么,再找它没防住的地方。这个思路在后面的SQL注入、越权、逻辑漏洞里全部用得上。这篇内容我会按实战的顺序来写,从环境准备到每个场景的具体操作路径,包括抓包、设置爆破参数、判断结果、以及我在实际练习中踩过的一些坑。如果你已经会基本的Burp抓包,可以直接跳到第三个场景开始看;如果刚接触,建议从第一个场景一步步走,因为后面很多操作是叠加上去的。2. 动手前的准备工作:环境、工具、字典2.1 把靶场跑起来Pikachu靶场通常是基于PHP环境的本地Web应用,很多学习者在Windows上用集成环境(比如小皮面板这类)直接部署,也可以用Docker拉一个现成的镜像跑起来。我个人更推荐后者,因为清理起来干净,出了问题重新起一个容器就行,不会把本机环境搞得乱七八糟。启动之后确认三个东西能访问:靶场首页、靶场的数据库初始化页面、以及目标漏洞页面本身。很多第一次练爆破的读者会遇到一个问题:页面能开,但是点提交之后数据库报错,这就说明靶场依赖的数据库没初始化,去执行一次初始化脚本就好了。浏览器这边,要把代理指向本机Burp监听的端口,同时关掉浏览器的自动填充密码功能,后面抓包会干净很多。这个细节别忽略,自动填充经常会往请求里带出一些额外的参数,干扰你对请求结构的判断。2.2 Burp Suite的配置要点爆破依赖的核心功能是Intruder,使用前建议先确认一下Burp Suite的版本和加载方式。社区版也能用Intruder,只是爆破速度有限制,对靶场这种低难度场景其实完全够用,不需要一上来就考虑专业版。需要提前设置的选项有两个:一是User options里的Connections,把超时时间稍微调大一点,防止靶机响应慢导致连接被判断为异常;二是Intruder的Resource pool,建议把并发线程控制在5以内。可能有人会觉得线程开多一点更快,但实际上靶场本地环境扛不住高并发,线程开大了反而会出现响应超时、数据错乱,结果反而不准。2.3 字典不用贪大,但要有结构爆破效果很大程度取决于密码字典。直接去下载一个几千万条的超大字典,在靶场这种简单场景里其实是浪费。建议准备一个200到500条左右的高频弱口令字典,里面包含常见密码、拼音组合、键盘序列、带年份的数字组合就够了。我一般会按这个思路组织字典:先放纯数字组合(123456、123456789、888888这类),再放拼音组合(admin、password、123qwe),然后放键盘序列(qwerty、1qaz2wsx),最后放带简单变化的密码(admin123、admin888、test123)。用户名通常就是admin、test这类固定值,不需要做用户名枚举,靶场这个模块本身也不考那个点。3. 最基础的那种爆破:基于表单的暴力破解3.1 抓包,先看清请求长什么样打开基于表单的暴力破解页面,随便填一组用户名密码提交。回到Burp的HTTP history里,找到对应的POST请求。这里要注意,如果页面有前端校验逻辑,比如空密码提示,那得先在Burp里把浏览器拦截关掉或者直接绕过前端校验再抓包,不然看不到真实请求。请求里最关心的就是POST body中的参数,比如username=admin&password=123456。这决定了爆破时payload要打的位置。很多初学者一上来就把整个请求丢进Intruder,然后发现结果全是同一个响应长度,根本分不清哪次是成功的,原因就是没在payload position里标对位置。3.2 Intruder的两种模式怎么选Intruder里有多个attack type,这个场景用Sniper和Pitchfork比较常见:Sniper模式:一次只爆破一个位置,适合固定用户名、爆破密码的场景。把密码参数的值选中,点击Add标记为payload position,用户名保持固定值。Pitchfork模式:可以同时爆破多个位置,每个位置对应一个字典。适合既不知道用户名也不知道密码的场景,但需要准备两个字典文件,并且两个payload set要一一对应——它按行配对,不是做笛卡尔积。3.3 怎么判断爆破成功了爆破结果不能只看状态码,靶场环境登录失败通常也返回200,所以要从两个维度判断:响应长度和响应内容。打开Intruder的Attack窗口,跑完之后按Response length排序,长度明显不同的那条就要重点看。如果还是不确定,直接点开该条记录的Response,搜一下有没有"success"、"登录成功"这类关键词。这里的坑是:有的靶场版本失败时也会返回200并且带一些提示文字,长度差异可能不大,那就需要在Grep-Match里提前配置关键词,比如配置"success"和"failed",跑完之后一眼就能看到哪条命中。这一步是整个爆破练习中最容易让人怀疑人生的环节,希望大家多花点时间理解响应差异判断的思路,这比爆破本身更有价值。4. 验证码不是绝对防护:两种绕过思路的完整复盘4.1 先理解验证码为什么会失效很多初学者把验证码当成"一定安全"的东西,其实验证码在Web应用里能不能起到防护作用,完全取决于服务端的校验逻辑怎么写。Pikachu靶场的验证码模块设计了两类典型缺陷,正好对应两种完全不同的绕过方式。第一类问题出在客户端:验证码的校验逻辑写在了前端JavaScript里,服务端根本不校验验证码。这种情况你直接看前端代码就能发现,验证码只是"看起来在验证"。第二类问题出在会话管理:服务端确实校验验证码,但验证码生成之后没有失效机制,同一个验证码在会话有效期内可以反复使用,你只要在爆破前手动填一次正确的验证码,然后保持这个会话去爆破就行。4.2 基于客户端的验证码绕过:直接爆破,不用管验证码打开对应页面,按F12看页面源码,搜索"verify"或者"check"这类关键字,你会发现验证码的校验函数在本地。这意味着验证码的判定结果完全由前端控制,攻击者只需要绕过前端脚本,直接把请求发给服务端,验证码字段随便填都不会被检查。实操路径是这样的:先正常抓一次提交请求,把请求体里验证码的参数单独标记出来,然后在Intruder里把验证码参数设为固定值,比如填个"1",同时把密码参数设为payload position。也就是说,验证码在这个请求里就是一个没用的字段,服务端根本没有取它的值去校验。4.3 基于客户端的验证码绕过:我要不要专门写一段Python脚本模拟请求当然可以,而且我建议你试一次。用Python脚本模拟整个爆破流程,本质上就是通过脚本发送HTTP请求,绕开浏览器的前端校验,把用户名、密码、验证码字段循环替换。脚本逻辑非常简单,一个for循环加一个requests.post就可以搞定。写脚本的好处是你能更直观地看到"验证码根本没进服务端校验逻辑"这个过程。4.4 基于服务端的验证码绕过:核心是"不刷新"服务端校验验证码的场景会稍微复杂一点,但突破点更典型。靶场这个场景里,验证码在服务端会校验,但不会在每次校验后失效——也就是说你第一次请求时填了一个正确的验证码,后续所有请求只要带着这个验证码,服务端都会认为正确。操作上分三步:先正常打开页面获取验证码图片,手动识别一次,填进表单;然后抓包,把验证码这个参数的值固定为刚才识别出来的结果;最后用Intruder对密码进行爆破。这条链路能跑通的核心原因是:验证码只绑定会话,不绑定使用次数,也没有过期机制。4.5 两类缺陷的对照:为什么说验证码设计是个"系统工程"把两类场景放在一起看,会发现它们是从不同层面失效的。前端的验证码失效,本质上是因为逻辑放错了层——校验放在客户端等于没有防护;后端的验证码失效,是因为校验逻辑缺少状态管理——生成了验证码、校验了验证码,但没把"已使用"的状态记录下来。真正合格的验证码防护应该满足三个条件:服务端生成、服务端校验,验证码使用后立即作废,验证码与用户会话、时间窗口绑定。靶场这个模块的价值就在于:它把一个本来应该做到位的方案拆成了缺陷案例,让你从攻击视角理解防御逻辑的每一个组成部分。5. Token防爆破:带动态参数的请求还能不能打5.1 Token机制的原理与真实难度Token防爆破是靶场暴力破解模块里最有含金量的一关。它的思路是:每次加载登录页面时,服务端生成一个随机token,嵌入到表单的隐藏字段里。提交登录请求时,客户端必须把这个token带回服务端,服务端校验token合法才会继续处理用户名密码。这个机制打掉了最朴素的爆破方式:因为每次请求的token都是新的,你如果拿一个固定的旧请求去重放,服务端一查token不匹配就直接拒绝。单次请求单独看,攻击者面临的是一个"每次都要从页面取新token再提交"的问题。5.2 手工爆破的思路转变面对token防爆破,常规的Intruder直接爆就行不通了,需要转变思路:能不能在每次爆破请求前,先去访问一次登录页面,把新鲜的token取出来,然后把它填到提交请求里?我最早尝试的方案是用Burp的宏功能,也就是Session Handling Rules。配置的大致逻辑是:在Intruder发起每个请求之前,先触发一个预先录制好的GET请求,从登录页面的响应中提取token参数,替换到当前请求的token字段里。这样一来每次爆破用的token都是最新获取的,等同于模拟了一个正常的浏览器行为。配置过程中最容易踩的坑是正则表达式提取。Burp里用正则提取token值时,需要准确匹配隐藏字段的格式,比如name="token" value="xxxxx",写正则时务必考虑双引号转义。我第一遍测试时,正则没匹配到值,导致每次请求的token都是空的,结果所有响应全是失败。排查了半天才发现是正则表达式里没有处理value后面的空白符。5.3 用Python脚本爆破token场景如果觉得Burp的宏配置不好调试,也可以直接写Python脚本。逻辑和上面一样,只不过由代码来管理"取token—提交请求"的过程:import requests import re session = requests.Session() base_url = "http://192.168.x.x/pikachu/" for pwd in ["123456", "admin888", "password"]: resp = session.get(base_url + "login_page") token_match = re.search(r'name="token" value="([^"]+)"', resp.text) token = token_match.group(1) data = { "username": "admin", "password": pwd, "token": token, } result = session.post(base_url + "login_submit", data=data) if "success" in result.text: print("成功密码:", pwd) break这段脚本不复杂,但它把token防爆破的本质表达得非常清楚:你只需要保证每次提交前拿一次新token,爆破就从"不可能"变成了"慢一点但可行"。5.4 为什么token防爆破依然可以被绕过从靶场这个场景能看出,token防爆破的强度取决于两个关键点:token是否绑定用户会话,以及token是否一次性使用。如果做到了这两点,爆破基本可以放弃;但如果只做到了"页面里有token",却没把token和具体会话绑定、或者token更新不够及时,那么用宏或者脚本就能轻松绕过。练习这个场景时,我还会建议大家顺手试一下:如果token生成规则有规律可循,比如是时间戳加固定位数,那么也可以尝试预测token,而不需要每次去页面获取。不要认为这是偶然情况,实际系统里token生成算法存在弱熵的情况并不罕见。6. 从进攻视角回到防守视角:爆破练习带来的几个防御启示6.1 爆破的本质是"枚举可能取值"暴力破解的本质是用枚举替代穷举:攻击者不知道正确的口令,但可以遍历候选口令空间。防御者的思路因此非常清晰:要么让枚举的代价高到不划算,要么让每次枚举的结果都不可复用。我在做完靶场练习后,对验证码设计的看法发生了很大变化。以前总觉得验证码影响用户体验,能不加就不加;练习之后发现验证码不是可有可无的装饰,而是防止自动化攻击的关键屏障。真正设计合理的验证码,必须做到服务端校验、使用后作废、有效时间短、与用户会话绑定。少任何一个条件,验证码的防线都会被击穿。6.2 登录接口是暴力破解的集中攻击面所有涉及口令验证的接口,都应该默认存在被爆破的风险。防御上常见的措施包括登录失败次数锁定、IP维度限速、账号维度锁定、以及双因素认证。我做爆破练习时,为了模拟真实情况,会在本地写一个简单的登录接口测试这些策略,这比单纯在靶场上爆破更有意思。需要注意的坑是:锁定策略如果只锁定账号不锁定IP,攻击者可以换着IP继续撞;只锁定IP不锁定账号,也没法阻挡分布式来源的攻击。两种策略需要配合。6.3 爆破能力对防御者依然关键最后说一点个人体会:很多人学了爆破之后觉得"不过如此",但真到做防御评估的时候,爆破测试反而是最容易找出系统弱点的入口之一。你不需要真的攻破什么,只需要证明登录接口没有做防枚举限制,就足以写进一份有分量的安全报告中。从靶场出来后,我建议大家找一个自己写的或测试用的Web应用,把同样的流程(未加防护版本、加验证码版本、加token版本)各走一遍,亲手对比一下攻击成本的变化。这种体感上的差异,比任何漏洞描述都更能让一个开发人员记住"为什么需要防护"。