
1. 项目概述这不是“黑客教程”而是一次面向安全从业者的防御能力验证实验“暴力破解实验步骤”这个标题在当前网络环境中极易引发误解。我必须开宗明义地强调本文所描述的是在完全合法授权、封闭可控环境内由专业安全人员执行的渗透测试模拟环节其唯一目的是检验系统自身的认证机制健壮性、发现设计缺陷、验证防护策略有效性。它不适用于任何未经授权的系统也不代表鼓励或教唆攻击行为——这就像消防员进行火场演练不是为了纵火而是为了更精准地扑灭真实火灾。核心关键词“暴力破解”在此语境下特指一种穷举式密码验证尝试的技术手段其本质是系统性地枚举所有可能的凭证组合如用户名密码并提交给目标服务进行校验。它之所以被列为安全测试的常规项目是因为现实中大量系统仍存在弱口令、无锁机制、响应时间无差异等基础性漏洞而这些漏洞恰恰是真实攻击者最常利用的突破口。因此这个实验的真正价值不在于“如何攻破”而在于“如何证明自己没被轻易攻破”。适合阅读本文的是三类人第一类是正在学习信息安全的在校学生需要理解攻击面与防御面的底层逻辑第二类是负责系统运维或应用开发的工程师需要知道自己的登录接口在真实压力下会暴露哪些隐性风险第三类是企业安全负责人需要评估现有防护体系是否经得起基础强度的考验。如果你属于这三类中的任何一类那么接下来的内容将不是教你“怎么黑”而是帮你“怎么防得更牢”。我做过不下二十个类似场景的模拟测试从高校教务系统到某制造企业的设备管理平台每一次复盘都印证了一个事实90%以上的“被暴力破解”事件根源不在算法多高深而在于基础配置有多松懈。所以我们今天要拆解的是一套可落地、可复现、可量化的验证流程而不是一个虚无缥缈的“技术神话”。2. 实验整体设计与思路拆解为什么必须“可控”、“可测”、“可逆”2.1 核心设计原则三个不可妥协的铁律任何一次有效的暴力破解模拟都必须建立在三条不可动摇的原则之上。这三条原则不是技术选型的参考项而是整个实验能否成立的前提条件。第一授权闭环原则。实验前必须获得书面授权明确测试范围如仅限于测试账号testuser的登录接口、时间窗口如仅限工作日9:00-17:00、数据边界如禁止访问用户数据库仅记录接口响应状态码与耗时。我曾参与过一个项目客户口头同意“随便测”结果我们在扫描过程中触发了对方WAF的误报规则导致生产环境API网关短暂熔断。事后复盘才发现对方根本没有开通测试白名单IP也没有告知我们其风控系统存在“5分钟内3次失败即封源IP”的隐藏策略。这个教训让我彻底放弃任何“默认授权”的侥幸心理——没有白纸黑字的授权书就没有实验的合法性也就没有后续所有技术动作的价值。第二环境隔离原则。绝对禁止在生产环境、预发布环境甚至与生产共享数据库的测试环境里执行。理想方案是搭建一套与生产配置完全一致但数据脱敏的“影子环境”。例如用Docker快速拉起一个NginxPHPMySQL的栈其中MySQL的数据表结构与生产一致但所有用户密码字段均替换为统一的哈希值如$2y$10$abc...且只保留3个测试账号。这样做的好处是既能真实模拟业务逻辑比如登录成功后跳转的URL、返回的JSON字段又确保即使脚本失控也不会泄露任何真实凭证。我习惯用docker-compose.yml定义这套环境启动命令一行搞定销毁也只需docker-compose down比手动搭环境快五倍也干净十倍。第三行为可逆原则。实验中所有操作必须能被完整回滚。这包括但不限于修改的配置文件必须有备份如nginx.conf.bak临时开启的日志级别必须在实验后恢复测试账号的密码策略如临时关闭复杂度要求必须重置。最典型的反例是某次测试中我们为绕过图形验证码临时在后端代码里注释掉了校验逻辑但测试结束后忘记还原导致上线后真实用户全部无法登录。这种低级错误根源就在于缺乏“可逆性”意识。因此我的标准操作是每执行一条可能影响系统状态的命令就紧跟着写一条对应的“还原命令”并用# REVERT:标注形成天然的操作对。2.2 方案选型逻辑为什么不用“全自动神器”而坚持“半手工脚本化”市面上有很多号称“一键暴力破解”的图形化工具比如某款流行的安全测试平台点几下鼠标就能跑完。但我坚持在教学和实际项目中采用“Python脚本curl命令人工观察”的组合方案。原因很实在自动化工具像一把钝刀它能砍倒树但你看不清年轮而半手工方案像一把刻刀它慢但每一刀下去你都清楚自己在雕琢什么。具体来说全自动工具的三大硬伤直接决定了它不适合作为学习和深度验证的载体黑盒不可见你不知道它内部如何构造请求头、如何处理重定向、如何解析响应体。当测试失败时你只能看到“爆破失败”却无法判断是目标接口返回了403、还是你的User-Agent被WAF拦截、抑或是CSRF Token过期。而用Python写requests.post()每一个参数、每一次response.status_code、每一行response.text都暴露在你眼皮底下调试起来像看自家厨房炒菜一样直观。策略僵化绝大多数工具内置的字典和线程策略是通用模板无法适配特定场景。比如某政务系统的登录接口要求每次请求必须携带一个动态生成的timestamp参数且有效期仅30秒。全自动工具根本无法实时计算并注入这个参数而一段20行的Python脚本调用int(time.time())就能搞定。干扰过大全自动工具往往默认开启高并发如100线程这对目标服务器是巨大压力。在一次对某高校选课系统的测试中我们用工具以50线程发起请求结果不仅没爆破成功反而把教务处的负载均衡器打到了85% CPU占用触发了自动扩容告警。后来改用单线程随机延迟time.sleep(random.uniform(0.5, 2))同样完成了测试却没惊动任何监控系统。因此我的方案是用Python控制核心逻辑请求构造、响应分析、结果记录用curl做快速验证比如curl -v -X POST -d userapassb http://test.local/login用人工做关键决策比如观察到第100次请求后响应时间突增500ms立刻暂停怀疑触发了速率限制。这种“人机协同”模式效率未必最高但可控性、可解释性、可学习性是任何黑盒工具都无法比拟的。2.3 影响范围预判一次实验能揭示多少层风险很多人以为暴力破解实验只关乎“密码强弱”这是巨大的认知偏差。一次严谨的实验实际上是一把多棱镜能折射出至少五个层面的系统健康度问题。我在某次金融类APP的渗透测试中就通过一次看似简单的登录接口爆破串联起了从网络层到应用层的完整风险图谱。第一层网络层防护有效性。这体现在请求是否被WAF/防火墙直接拦截。如果前10次请求就收到403 Forbidden或连接被重置Connection reset说明基础网络防护是生效的。但如果请求能顺利抵达应用服务器只是返回200 OK但内容为“登录失败”那就意味着WAF的规则库可能过于宽松或者根本没有针对登录接口设置专门的防护策略。第二层应用层认证逻辑缺陷。这是最核心的一层。典型问题包括响应时间无差异无论输错用户名还是密码响应时间都是320ms±10ms无法通过时间侧信道判断用户名是否存在错误提示过于详细“用户名不存在” vs “密码错误”前者直接帮攻击者完成了用户名枚举未启用二次验证如短信、邮箱确认导致单一密码成为唯一防线。第三层会话管理安全性。暴力破解成功后系统是否生成了新的会话Token这个Token是否绑定IP、User-Agent、设备指纹我曾在一个电商后台系统中发现爆破成功后返回的Cookie中sessionid字段是纯数字递增的如1001, 1002, 1003这意味着只要猜中一个有效ID就能直接冒用其他管理员会话。这比密码本身被破解更危险。第四层日志与监控覆盖度。实验过程中所有请求是否被完整记录日志中是否包含源IP、请求时间、请求路径、响应状态码、响应大小更重要的是是否有告警机制比如同一IP在5分钟内出现20次401 Unauthorized是否能触发邮件或短信通知很多系统日志写了但告警规则是空的形同虚设。第五层应急响应流程成熟度。这是最容易被忽视却最关键的一层。当测试团队向客户通报“我们已成功获取管理员权限”时客户的安全部门能否在15分钟内定位到攻击源IP、30分钟内完成会话踢出、1小时内给出初步根因分析一次实验的价值最终要落到这个闭环上。否则再漂亮的PoC概念验证也只是纸上谈兵。3. 核心细节解析与实操要点从环境准备到结果解读的全链路拆解3.1 环境准备三步搭建零风险测试沙箱搭建一个安全、干净、可复现的测试环境是整个实验成功的基石。我把它拆解为三个不可跳过的步骤每个步骤都有其不可替代的作用。第一步容器化部署目标服务。我强烈推荐使用Docker而非虚拟机或物理机因为它的轻量化和一致性无可替代。以一个典型的Web登录系统为例我的docker-compose.yml核心配置如下version: 3.8 services: web: image: nginx:alpine ports: - 8080:80 volumes: - ./html:/usr/share/nginx/html - ./conf/nginx.conf:/etc/nginx/nginx.conf depends_on: - php php: image: php:8.1-apache volumes: - ./src:/var/www/html - ./php.ini:/usr/local/etc/php/php.ini environment: - MYSQL_HOSTdb - MYSQL_USERtest - MYSQL_PASSWORD123456 depends_on: - db db: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORDrootpass - MYSQL_DATABASEtestdb - MYSQL_USERtest - MYSQL_PASSWORD123456 volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql这里的关键细节在于./init.sql——它不是导入生产数据而是创建一个极简的测试表CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password_hash VARCHAR(255) NOT NULL ); INSERT INTO users (username, password_hash) VALUES (admin, $2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi), -- password password (testuser, $2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi), (guest, $2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi);注意所有密码哈希都使用同一个值这是刻意为之。它确保了测试的公平性我们不是在比谁的密码更弱而是在验证系统对“弱密码”的防御能力。同时php.ini中我一定会关闭display_errors Off防止错误信息泄露路径或版本号这本身就是一道基础防线。第二步配置最小化攻击载荷。所谓“载荷”就是我们用来发起请求的工具集。我坚持“够用就好”原则绝不堆砌。核心就三样curl用于快速手工验证单个请求。比如测试基础连通性curl -s -o /dev/null -w %{http_code} http://localhost:8080/login.php输出200说明服务可达。Python 3.8作为主控脚本语言。我只安装两个必要库requests发HTTP请求和rich美化终端输出让进度条和表格一目了然。自定义字典文件这是最易被忽视却最影响效率的部分。我绝不用网上下载的“万能字典”而是根据目标场景定制。例如针对一个高校系统我会生成一个university_users.txt内容是常见学号格式如20201101,20201102...和教职工工号如JZ20001,JZ20002...密码字典则分三层第一层是top10.txtpassword, 123456, admin...第二层是seasonal.txtsummer2023, winter2023...第三层是company_name.txt如果系统属于某公司则加入公司名123, company2023等。这种分层字典比盲目扫10GB大字典效率高出一个数量级。第三步部署监控与审计探针。没有监控的实验就像蒙眼开车。我在目标容器内会额外挂载一个日志收集脚本# 在php容器的entrypoint中添加 tail -f /var/log/apache2/access.log /var/log/apache2/error.log | \ while read line; do echo $(date %Y-%m-%d %H:%M:%S) [ATTACK-TEST] $line /tmp/attack_audit.log done 同时在宿主机上运行一个独立的watch命令实时监控这个审计日志watch -n 1 tail -n 20 /path/to/docker/volumes/attack_test/_data/tmp/attack_audit.log | grep POST /login.php这样当我运行爆破脚本时左手边是Python的进度条右手边是实时滚动的原始请求日志哪个IP、什么时间、提交了什么参数、服务器返回了什么一清二楚。这种“双屏监控”模式让我在5分钟内就能定位到90%的问题。3.2 请求构造与响应分析如何读懂服务器的“潜台词”暴力破解的本质是与服务器进行一场精密的“对话”。服务器不会直接告诉你“密码错了”但它会通过各种细微的“潜台词”泄露信息。能否听懂这些潜台词决定了实验的成败。请求构造的四大黄金参数缺一不可User-Agent必须伪装成主流浏览器。我固定使用Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36。原因很简单很多WAF的默认规则会对python-requests或curl这类工具的默认UA直接拦截。一次真实的测试中我们把UA改成Chrome后QPS每秒查询率从0飙升到30这就是UA的威力。Referer必须设置为登录页面的URL。比如如果登录表单在http://test.local/login.html那么请求头里必须有Referer: http://test.local/login.html。这是为了绕过一些基础的CSRF防护因为很多老系统会检查Referer是否来自自己的域名。Content-Type根据表单编码方式选择。如果是传统form enctypeapplication/x-www-form-urlencoded就用application/x-www-form-urlencoded如果是Ajax提交的JSON则用application/json。我见过太多脚本因为Content-Type写错导致服务器直接返回400 Bad Request白白浪费测试时间。动态Token处理这是最高频的坑。现代系统几乎都有Anti-CSRF Token。我的处理流程是先GET一次登录页用正则或BeautifulSoup提取input namecsrf_token valueabc123中的value然后在POST请求中把这个value作为参数提交。Python代码片段如下import re import requests session requests.Session() # 第一步获取登录页提取token login_page session.get(http://localhost:8080/login.php) token_match re.search(rinput[^]name[\]csrf_token[\][^]value[\]([^\])[\], login_page.text) if not token_match: raise Exception(CSRF token not found) csrf_token token_match.group(1) # 第二步构造POST数据 data { username: admin, password: password, csrf_token: csrf_token } response session.post(http://localhost:8080/login.php, datadata)响应分析的三大关键信号比状态码更重要HTTP状态码这是最表层的信号。200 OK不一定成功可能是返回了错误页面302 Found也不一定失败可能是登录成功后跳转。我见过一个系统登录成功返回302跳转到/dashboard.php失败却返回200并显示错误提示。所以永远不要只看状态码。响应体内容长度Content-Length这是最可靠的信号之一。我习惯用len(response.content)来统计。在一次测试中所有失败响应的长度都是1245±5 bytes而成功响应是3892 bytes。这个400字节的稳定差值成了我判断成功的唯一依据。用curl -s -o /dev/null -w %{size_download}就能快速验证。响应时间Response Time这是最隐蔽的信号。我用response.elapsed.total_seconds()精确测量。如果输错密码服务器需要查询数据库、比对哈希、生成错误页面耗时可能在350ms而输错用户名可能直接在内存里查表失败耗时仅80ms。这个时间差就是“用户名枚举”的突破口。我通常会设置一个阈值比如if response.elapsed.total_seconds() 0.3:就标记为“可能密码正确”。提示永远用response.content而非response.text来分析响应体。因为text会尝试解码如果服务器返回乱码或二进制内容会导致Python抛出UnicodeDecodeError中断整个脚本。而content是原始字节流稳定可靠。3.3 实验执行与结果解读从“爆破成功”到“风险定级”的完整闭环实验执行不是按下回车键就完事而是一个需要持续观察、动态调整、深度解读的闭环过程。我把整个过程分为四个阶段每个阶段都有明确的目标和交付物。第一阶段基线探测10分钟。目标是摸清目标接口的“脾气”。我手动执行5-10次curl请求覆盖不同场景curl -X POST -d uadminpwrong http://localhost:8080/login.php错误密码curl -X POST -d uwrongppassword http://localhost:8080/login.php错误用户名curl -X POST -d uadminp http://localhost:8080/login.php空密码curl -I -X GET http://localhost:8080/login.phpHEAD请求看Server头记录下每次的响应状态码、Content-Length、响应时间、响应体中的关键字符串如invalid credentials、user not found。这个基线数据将成为后续自动化脚本的判断依据。比如如果发现错误用户名和错误密码的响应长度完全一致那就可以断定该系统不存在用户名枚举漏洞后续字典攻击可以放心聚焦在密码上。第二阶段字典分层攻击30-60分钟。基于基线数据启动Python脚本。我的脚本采用严格的分层策略# 第一层Top 10 密码针对所有用户名 for username in usernames: for password in top10_passwords: if try_login(username, password): print(f[SUCCESS] {username}:{password}) break # 找到一个就停避免过度请求 # 第二层季节性密码仅针对已知存在的用户名 for username in known_valid_users: # 从第一层结果中获取 for password in seasonal_passwords: if try_login(username, password): print(f[SUCCESS] {username}:{password}) # 第三层公司名衍生密码同上 for username in known_valid_users: for password in company_derived_passwords: if try_login(username, password): print(f[SUCCESS] {username}:{password})关键技巧在于try_login()函数里的“熔断机制”如果连续5次请求的响应时间都超过1秒或者连续3次收到503 Service Unavailable脚本会自动暂停30秒并发送告警。这既保护了目标服务也避免了因服务不稳定导致的误报。第三阶段结果交叉验证15分钟。一旦脚本报告“成功”绝不轻信。我必须进行三重验证手工复现用浏览器打开登录页输入脚本找到的凭证看是否真能进入后台。Token有效性验证用curl -b PHPSESSIDxxx带上返回的Cookie访问一个需要登录态的页面如/admin/info.php看是否返回200。权限边界测试尝试访问/admin/delete_user.php?id1看是否被拒绝403确认拿到的只是一个普通用户权限而非越权。只有三重验证全部通过才认定为一次有效的“爆破成功”。第四阶段风险定级与报告生成20分钟。这是实验价值的最终体现。我用一张简单的表格将技术发现转化为业务语言风险项技术描述业务影响修复建议严重等级弱口令普遍存在测试账号admin使用密码password可直接登录攻击者可轻易获取系统最高权限篡改数据、窃取信息强制密码复杂度策略大小写字母数字符号长度≥8启用登录失败锁定5次失败锁定30分钟高响应时间无差异输错用户名与输错密码平均响应时间均为320ms攻击者可进行高效用户名枚举大幅降低爆破难度对用户名不存在的情况增加固定延时如sleep(500ms)使所有错误响应时间一致中CSRF Token缺失登录请求无需携带任何Anti-CSRF Token攻击者可构造恶意网页诱导用户点击实现静默登录在登录表单中嵌入一次性Token并在服务端严格校验高这张表就是给客户CTO看的“风险地图”。它不讲技术细节只讲“会发生什么”和“该怎么补”。这才是安全工作的终极目标。4. 常见问题与排查技巧实录那些文档里不会写的“血泪经验”4.1 典型问题速查表从“连不上”到“爆不破”的全场景应对在数十次实战中我总结出一份高频问题速查表。这些问题90%都源于环境配置或认知偏差而非技术难题。表格按发生频率排序附带我的独家排查口诀。问题现象可能原因排查口诀我的实操心得curl: (7) Failed to connect to localhost port 8080: Connection refusedDocker容器未启动或端口映射错误“先docker ps再docker logs”这是最常见的新手坑。我养成习惯每次docker-compose up -d后必跟一句docker ps | grep web确认容器状态是Up。如果看到Restarting立刻docker logs attack_web_1看启动日志90%是Nginx配置语法错误。脚本运行后无输出CPU占用100%字典文件路径错误导致open()抛出异常但被except: pass吞掉“加一行print(Loading dict...)再加一行print(len(wordlist))”我曾经调试一个脚本整整两小时最后发现字典文件名写成了password.txt而实际是passwords.txt。从此我的所有脚本开头必加两行诊断打印这是最廉价的“保险丝”。所有请求都返回200 OK但响应体是登录页HTML目标接口未正确处理POST或参数名错误“用curl -v看完整请求头和响应头重点找Set-Cookie和Location”有一次我发现响应头里有Set-Cookie: PHPSESSIDabc123但响应体却是登录页。这说明服务器其实创建了会话但没跳转。原因是我POST的参数名是user而实际接口期望的是username。curl -v的详细输出救了我半天时间。成功登录后用返回的Cookie访问其他页面返回403 ForbiddenSession未正确绑定或服务端做了IP/User-Agent绑定“用curl -b cookie -H User-Agent: xxx把登录时的UA原样带上”某次测试中我拿到Cookie后用curl -b访问一直403。最后发现服务端代码里有一行if ($_SERVER[HTTP_USER_AGENT] ! $_SESSION[ua]) die(403)。原来它把第一次登录的UA存进了Session。所以后续所有请求UA必须和登录时完全一致。脚本运行缓慢QPS不到1DNS解析慢或未启用连接复用“加--http1.1和--keepalive参数或在Python中用Session()”默认curl每次请求都新建TCP连接开销巨大。加上--http1.1 --keepalive后QPS从3飙升到80。Python里requests.Session()对象会自动复用连接这是性能提升的基石。4.2 独家避坑技巧那些踩过三次才悟出的“反直觉”真相有些经验是教科书和文档永远不会写的因为它们违背常识却无比真实。以下是我在实战中用“踩坑”换来的三条铁律。技巧一“慢”才是最快的。初学者总想提高线程数追求“一秒千请”。但我的经验是在未摸清目标“脾气”前单线程随机延迟是最高效的选择。为什么因为高并发会触发WAF的速率限制、服务器的连接池耗尽、甚至数据库的锁等待导致大量请求超时或返回错误响应这些错误响应会污染你的基线数据让你无法准确判断什么是“真正的失败”。我现在的标准是基线探测阶段用time.sleep(1)正式攻击阶段用time.sleep(random.uniform(0.5, 1.5))。表面看慢实则稳一次到位省去无数返工时间。技巧二“失败”比“成功”更有价值。很多人盯着“爆破成功”那一刻却忽略了失败响应里的宝藏。比如一次测试中所有失败请求的响应头里都包含X-Debug-Mode: on这暴露了服务器开启了调试模式可能泄露更多敏感信息。另一次失败响应的HTML里有注释!-- Dev note: temp fix for SQLi, remove before prod --这直接指向了一个未修复的SQL注入漏洞。所以我的脚本里有一个专门的analyze_failure()函数它会扫描每一次失败响应的Header和Body寻找这类“意外馈赠”。真正的安全高手不是总在找钥匙而是善于从锁孔的锈迹里读出整扇门的材质。技巧三“合法”是最高级的技巧。所有技术手段都必须匍匐在法律和授权之下。我有一个雷打不动的习惯每次实验开始前打开一个空白文本文件写下三行字授权编号[客户提供的编号] 测试范围[精确到URL路径如/login.php] 截止时间[YYYY-MM-DD HH:MM]然后把这个文件和本次实验的所有日志、截图、报告一起打包命名为[授权编号]_report.zip。这不是形式主义而是给自己划下红线。当某次测试中我发现一个超级漏洞激动得想立刻深入挖掘时我打开这个zip包看到那三行字就冷静下来——我的授权范围只到登录接口。技术的边界永远由法律和契约划定而一个成熟的安全从业者他的第一道防火墙永远是自己的职业敬畏心。5. 实操过程与核心环节实现一份可直接运行的Python脚本详解5.1 脚本核心逻辑从“能跑”到“能用”的进化下面这份Python脚本是我经过十余个项目迭代打磨出的“最小可用版”。它不追求炫酷功能只保证在任何一台装有Python3.8和requests库的机器上修改3个变量就能立即运行得到可信结果。脚本全文不到150行但每一行都承载着实战经验。#!/usr/bin/env python3 # -*- coding: utf-8 -*- 暴力破解实验核心脚本 v1.2 作者某安全研究员 用途在授权范围内对Web登录接口进行可控、可审计的凭证验证测试 import requests import time import random import re import sys from rich.console import Console from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn console Console() progress Progress( SpinnerColumn(), TextColumn([progress.description]{task.description}), BarColumn(), TaskProgressColumn(), consoleconsole ) # 配置区只需修改这里 TARGET_URL http://localhost:8080/login.php # 目标登录接口URL USERNAMES_FILE usernames.txt # 用户名字典文件路径 PASSWORDS_FILE passwords.txt # 密码字典文件路径 # def load_wordlist(filepath): 安全加载字典文件自动处理BOM和空行 try: with open(filepath, r, encodingutf-8-sig) as f: words [line.strip() for line in f if line.strip()] console.log(f[green]✓[/green] 已加载 {len(words)} 个{filepath.split(.)[0]}) return words except FileNotFoundError: console.log(f[red]✗[/red] 错误找不到字典文件 {filepath}) sys.exit(1) def get_csrf_token(session, url): 从登录页HTML中提取CSRF Token try: response session.get(url, timeout10) response.raise_for_status() # 使用正则提取兼容大多数情况 token_match re.search(rinput[^]name[\]csrf_token[\][^]value[\]([^\])[\], response.text) if token_match: return token_match.group(1) else: console.log([yellow]⚠[/yellow] 未在登录页找到CSRF Token将使用空值) return except Exception as e: console.log(f[red]✗[/red] 获取CSRF Token失败: {e}) return def try_login(session, url, username, password, csrf_token): 尝试一次登录返回是否成功及详细信息 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: url.replace(/login.php, /login.html), # 假设登录页是login.html Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 } data { username: username, password: password, csrf_token: csrf_token } try: start_time time.time() response session.post(url, datadata, headersheaders, timeout15) end_time time.time() # 关键判断逻辑基于基线探测设定的阈值 # 这里假设我们已知成功响应长度约3892失败响应长度约1245 is_success False if response.status_code 200 and len(response.content) 3000: is_success True elif response.status_code 302 and dashboard in response.headers.get(Location, ): is_success True return { success: is_success, status_code: response.status_code, content_length: len(response.content), response_time: round(end_time - start_time, 3), redirect_url: response.headers.get(Location, ) } except requests.exceptions.Timeout: return {success: False, error: timeout} except requests.exceptions