本地弱口令名单能证明什么,为什么它不是全网撞库

「测一下这串密码有没有泄露」经常被说成同一件事。实际至少有三种做法:把明文交给网站、只发哈希前缀去查 Have I Been Pwned、在当前标签页对照一份公开高频名单。核对方法不是相信「本地」两个字,而是看这一次请求里走的是哪一种,以及命中、未命中各自能写进工单的哪句话。

先记结论:本地弱口令名单能支持的只有「这串字符像不像已被广泛滥用的口令」。未命中不能证明从未拖库;命中只说明太常见,应立刻更换。Have I Been Pwned 的密码范围查询是另一套协议,只发 SHA-1 前五位。把明文贴进任意「在线检测」框之前,先确认对方走的是哪一种。

一句话先回答搜索意图

本地弱口令名单能证明的,只有一件很窄的事实:你输入的这串字符,在「已经下载到当前标签页的那份公开高频名单」里能不能被匹配上。它证明不了「全网没见过」,也回答不了「我的邮箱有没有进过某次拖库」。Have I Been Pwned(HIBP)的 Pwned Passwords 范围查询是另一条路:本机算 SHA-1,只把哈希前五位发给 https://api.pwnedpasswords.com/range/{prefix},完整后缀在客户端比对。

这篇文章给要把旧口令拿去「测一测」的开发者、运维,以及被同事丢过来一条「在线密码检测」链接的客服。它不代替 密码检测 页的操作说明,也不重复「本地加密有没有上传明文」那一套 Network 总论。你要带走的是:三种查法分别离开本机什么、本地名单覆盖到哪一步变形、命中和未命中各自能写进工单的哪句话。

三种「查泄露」不是一回事

搜索「密码泄露查询」时,结果页会把完全不同的协议排在一起。有的站点把输入框里的字符直接 POST 出去;有的接入 HIBP 的 k-anonymity 范围接口;有的只下载一份短名单,在浏览器里做字典查找。三种都能在界面上显示「未发现」或「已泄露」,但请求行完全不同,能支持的结论也不同。

OWASP Authentication Cheat Sheet 把「拦截常见和已泄露口令」写成注册与改密流程的建议,并同时点名 Pwned Passwords 与 SecLists 一类公开列表。它没有把「本机短名单」和「十亿级哈希库」写成同一种证据。工单里若只写「查过了,没泄露」,接收方无法判断你用的是哪一张网。

做法 离开本机的是什么 未命中能写什么
明文或完整哈希上传 口令本身,或足以还原/比对的完整摘要 只证明对方站点这次没标红;证明不了安全
HIBP 范围查询 SHA-1 或 NTLM 的前 5 位十六进制 在 HIBP 当前索引里没找到这串哈希
本机公开高频名单 名单文件本身;待测密码留在标签页 这份短名单及其变形规则没对上

第三种的网络活动仍然存在:浏览器会 GET 名单文件。那是公开词表,不是你的口令。把这两种请求当成「都联网了所以都上传了」,会误判边界;反过来,把「页面写了本地」当成证据,也会漏掉第一种站点。

不要用正在使用的生产库口令、未轮换的 API 令牌或个人网银密码做实验。准备一串只有这次才有的假口令当金丝雀,例如 CanaryPwd-20260902-xK9。核对的是请求有没有带走这串字符,不是把真实秘密再暴露一遍。

本地名单实际对照什么

以 FastPwd 密码检测页为例,名单随页面从同源路径 data/leaked-top10k.txt 下载,再在本机建一张小写键到名次的表。文件名带 top10k,是常见公开高频列表的命名习惯;打开该请求的响应,当前能数到八百余条非空行,开头是 123456、password、qwerty 这类被反复拖库的口令。产品文案从不把它写成「一万条」或「全网拖库集合」。

查找不是只做一次全等比较。实现里按四步走,命中任一步就标红,并强制把等级打成弱。

  1. 精确匹配:输入与名单条目完全一致。
  2. 大小写折叠:先转成小写再查,Password 与 password 同一条。
  3. 去掉尾部 0–999:qwerty123 会先被收成 qwerty 再查。
  4. 常见 Leet:@/4→a,3→e,1/!→i,0→o,5/$→s,7→t;也可先去尾部数字再做这一步。

输入超过 128 位时,后两步变形不再跑,只保留精确与大小写。名单加载失败时,页面会改用内置最小集:password、123456、12345678、qwerty、admin123,并在状态行说明检测仍在本机完成。名次只用来写提示:前 100、前 500、前 1000,或退回「公开泄露名单」——它不是 HIBP 那种「出现过多少次」的计数。

NIST SP 800-63B-4 要求核验方在开户和改密时,把候选口令整串对照「常见、可预期或已泄露」的黑名单,而不是对照子串;同时写明不应再叠一套组成规则。本地高频名单符合「先挡住最常被猜的那一批」这一层,但名单体积远小于攻击者手里的完整字典,也远小于 HIBP 持续并入的拖库哈希。2026 年 6 月,HIBP 仅 June 2026 Stealer Logs 这一批就写入了约 1.24 亿个不重复密码。八百余条公开高频词能抓住 123456,抓不住只在某一次企业内部拖库里出现过的长口令。

HIBP 范围查询发什么、不发什么

HIBP 的密码页自己写着:没查到「并不一定说明这是好密码,只说明它不在本站索引里」。范围查询的协议写在 API v3 · Pwned Passwords。客户端先对口令做 SHA-1(或按 mode=ntlm 做 NTLM),取出前 5 个十六进制字符,请求 GET https://api.pwnedpasswords.com/range/{prefix}。服务端返回该前缀下的全部后缀和出现次数;完整 40 位哈希的后 35 位留在本机比对。

官方给出的量级可以当场对照:一次范围查询通常返回大约 800 条后缀;前缀空间是 165 = 1,048,576 个,从 00000 到 FFFFF,每一个都回 HTTP 200,不该出现 404。可选请求头 Add-Padding: true 会把响应垫到 800–1000 行,降低旁观者靠响应长度反推前缀的能力。Cloudflare 在 Validating Leaked Passwords with k-Anonymity 里把这套范围查询称为 k-匿名:服务端拿不到未泄露口令的完整哈希,因而也无法事后单独「解开」这一次查询。

邮箱查询是另一条路。官方写明:把邮箱直接放进路径会把该地址交给 HIBP;若要用 k-anonymity 查邮箱,发的是邮箱 SHA-1 的前 6 位,不是密码的前 5 位。把「我在 HIBP 查过了」写进工单时,要写清查的是密码哈希前缀,还是邮箱。两者隐私边界不同,结论也不同。

FastPwd 不调用上述任一 HIBP 接口。密码检测页能做的是本机熵估算加这份公开高频名单。需要对照十亿量级哈希库时,应使用 HIBP 官方页或自建离线副本,并自己打开 Network 确认请求行只有五位前缀。不要默认「安全工具都会帮你查 HIBP」。

用 Network 当场核对待测密码有没有出站

页面上的「不上传」不能当证据。金丝雀字符串可以。步骤与「本地加密有没有带走明文」同类,但观察目标换成口令字段和名单文件。

  1. 打开开发者工具 Network,勾选 Preserve log。过滤先留空,避免漏掉分析上报。
  2. 在密码检测页输入金丝雀,例如 CanaryPwd-20260902-xK9,等大约 280 毫秒后的本地计算结束。
  3. 在请求列表里搜这份金丝雀:请求行、Query、请求体、表单字段都不该出现它。
  4. 应当能看到一次同源 GET data/leaked-top10k.txt。打开响应,确认是一行一条公开口令,没有你刚输入的金丝雀。
  5. 若站点声明接入 HIBP,另找 api.pwnedpasswords.com/range/:路径里只该有五位十六进制,不该出现明文。

访问统计可以存在。FastPwd 的审计埋点只推事件名,例如「开始审计」「结果弱」「命中泄露列表」,不把输入框内容当作参数。在 Network 里打开指向 /tj/matomo.php 的上报,搜索金丝雀,应当没有命中。关闭标签页后,这次输入不会留在 FastPwd 服务器上——前提是它从未作为业务数据到达服务器。这一条要用刚才的搜索来支持,不能用「我关页面了」来支持。

若金丝雀出现在任何请求里,结论就翻转:无论按钮写「本地」还是「隐私」,这一次检测已经把口令送出标签页。换工具,或改用离线名单,再重复同一步骤。

命中和未命中分别能支持什么

命中时,工单里可以写实的句子是:「这串字符与随页面下载的公开高频名单在某一步匹配上了(精确 / 大小写 / 尾部数字 / Leet),按本机规则视为极常见弱口令,必须更换,且不要在其他站点复用。」不能写的是:「对方服务器确认这是某某年某次拖库的原文。」本机名单没有事件名、没有出现次数,只有名次提示。

未命中时,能写的更短:「在当前这份公开高频名单及其四步变形下未命中。」不能写:「从未泄露」「可以继续用」「比 HIBP 更干净」。一份八百余条的高频表,本来就不是为「证明安全」设计的。攻击者的喷洒字典、公司内部历史口令、只在一次私有泄露里出现的长句,都不会出现在这张表里。

熵估算是第三条平行信息。页面按长度和字符类估算 bits:≥ 80 为极强,≥ 60 为强,≥ 40 为中,其余为弱;离线穷举按约每秒 100 亿次、在线限流按约每秒 1000 次给出时间量级。长度不足 8 位或命中内置 Top 常见弱口令时,等级会被压到弱。这些数字用来比较相对强弱,不是倒计时承诺。命中公开名单时,熵再高也会被改写成弱——因为字典攻击不走「平均穷举」这条路。

常见问题

未命中弱口令名单,是不是说明密码从没泄露过?

不是。未命中只说明:在当前标签页这份公开高频名单里,精确匹配、大小写折叠、去掉尾部 0–999、常见 Leet 这几步都没对上。它不能证明这串字符从未出现在任何一次拖库、未出现在 Have I Been Pwned,也不能证明可以继续复用。

Have I Been Pwned 查密码会不会把明文发出去?

按官方 API:Pwned Passwords 范围查询只接收 SHA-1 或 NTLM 哈希的前 5 位,返回约 800 条后缀,完整比对留在客户端。明文和完整哈希都不应出现在请求里。用邮箱查泄露是另一套接口,直接提交邮箱会把该地址交给对方。以你打开 Network 看到的那一行为准,不以按钮文案为准。

FastPwd 的密码检测会不会查询 Have I Been Pwned?

不会。对照的是随页面下载到本机的公开高频名单。待测密码不作为 HTTP 业务数据发出,也不写入 analytics。访问统计只记「开始审计」「结果弱 / 中 / 强」这类事件名。打开即用,无需账号,站内也没有密码库可保存检测记录。

熵很高但命中名单,到底算不算强密码?

按本机规则,命中公开弱口令名单会强制标为弱。熵估算的是字符空间上的穷举成本,名单回答的是「攻击者字典里有没有这串」。两套问题不能互相抵消。NIST SP 800-63B-4 要求核验方对照常见或已泄露口令的整串黑名单,而不是只看复杂度或定期更换。

用假口令把步骤走完

若你希望用一个把计算边界写清楚的页面来练习,打开 FastPwd 的 密码检测。打开即用,无需注册。先输入 password 或 qwerty123,应当看到命中公开名单并强制为弱;再输入上面的金丝雀,应当未命中名单,同时 Network 里搜不到这串字符。名单文件那一次 GET 可以打开来数行数,和本文写的「八百余条高频词」对齐。

判定为弱之后,到 密码生成器 在本机抽一串新口令:随机模式长度 6–128 位,默认 16 位,低于 8 位会提示安全性较低。必须把新口令交给同事时,改走 阅后即焚:本机用 AES-256-GCM 加密,服务器只暂存密文,密钥放在链接的井号后面。不要把完整焚链再贴回任何一个「在线检测」输入框。

这些练习的目的不是证明某一个站点「绝对安全」,而是让你在按下检测之前,手里有一份可以给同事看的对照:这一次走的是明文上传、哈希前缀,还是本机短名单;命中时换掉,未命中时不要写成「从未泄露」。换任何「安全评分」或「泄露查询」扩展,步骤可以复用。

下次有人把密码丢进检测框时,先问三个问题。第一,请求里有没有明文或完整哈希。第二,对照的是八百条高频词,还是 HIBP 那种范围查询。第三,未命中时工单准备写哪一句。能重复的对照,才值得写进你们自己的口令规范。

延伸阅读

若还要确认「检测页自己有没有把原文送走」,用同一枚金丝雀走完更完整的 Network 步骤:浏览器本地加密怎么核对明文没有离开本机。必须把完整秘密交给另一个人、而不是交给检测框时,再看密钥为什么能放在井号后面:URL 的井号后面为什么适合放密钥,以及什么时候会失效。工单和日志要贴进 AI 之前,字段级对照见:把工单和日志贴进 AI 之前,怎么先核对哪些字段会离开本机。