猫工具站
帮助中心 猫Blog 关于我们 广告合作 EN

密码生成器和强度检测器分别算了什么:字符池、熵值档位与那个差十倍的单位

加解密更新于 2026-09-29

加解密分类下有密码生成器和密码强度检测器两页,很多人把它们当同一份鉴定报告的上下半页。其实两边量的不是一件事:生成器从不读它刚吐给你的那串字符,检测器从不知道你把勾选框调成了什么。下面的数字全部来自这两页的代码,凡是要紧的地方都在浏览器里直接调用页面自己的函数跑过一遍。

四套字符池,以及「排除相似」到底拿掉了什么

生成器只从四个写死的字符串里取字符:大写 A-Z 26 个,小写 a-z 26 个,数字 0-9 10 个,特殊符号 27 个——完整字面量是 !@#$%^&*()_+-=[]{}|;:,.<>?~。四套全开时字符池 89;页面加载时的默认状态是大写、小写、数字三项勾选、特殊符号与排除相似都不勾,池为 62。

「排除相似字符(0/O、1/l/I)」这个开关精确地只去掉标签上承诺的 5 个字形:大写少 O 和 I 变 24,小写少 l 变 25,数字少 0 和 1 变 8,符号一套一个没少。所以四套全开再勾排除,池从 89 变成 84,而不是从 89 变成「89 减 5」。

那份 27 个符号的名单里没有引号、没有反斜杠、没有斜杠、没有反引号也没有空格。好处是这页生成的密码天生不会破坏 shell 和 JSON;代价是你没法要求某个它名单之外的符号。整页能调的只有四个字符勾选框加一根长度滑块,滑块范围 4–64,默认 16,没有自定义字符集输入框。

每种字符先保证一个,再洗牌

它不是「随机抽 N 个字符然后祈祷」。代码先从每个启用的字符集各取一个,剩下的位置再从全部启用集合的并集里取,最后用 Fisher-Yates 打乱。每一次取数——保证位、填充位、洗牌位——都走同一个函数:

function secureRand(max) {
  var limit = Math.floor(4294967296 / max) * max;
  var buf = new Uint32Array(1);
  do { crypto.getRandomValues(buf); } while (buf[0] >= limit);
  return buf[0] % max;
}

这是对 crypto.getRandomValues 的拒绝采样:先把 32 位整数空间截到 max 的整数倍,落在倍数之外的值直接扔掉重取,所以不存在裸 % max 带来的取模偏置——不是「偏置被摊平」,而是偏置那部分根本没被采用。它用的是浏览器 CSPRNG,不是 Math.random()。

实测:四套全开、勾选排除、长度 16,连续调用 genOne() 300 次,得到 300 个长度恰为 16 的密码,每个都四类字符齐备,没有一个含 0/O/1/l/I,300 个互不重复。再测「四套全开 + 勾选排除 + 长度 4」:50 个样本全部是 4 个字符、且恰好一大写一小写一数字一符号。这时密码的形状是固定的,只有具体字形是随机的——长度 4 就还是 4 个字符的秘密,不会因为四类齐全而变成 16。

按一下「生成密码」实际发生 7 次取数:generatePassword() 先调一次填主输入框,再循环 6 次填下面那批批量项。批量那 6 条是独立生成,不是主密码的变体。四个勾选框全部取消时 genOne() 返回 null,按钮弹 请至少选择一种字符类型,不会给你一个空结果。

强度条描述的是设置,不是框里那串

生成器的熵值是 长度 × log2(字符池),输入只有滑块和勾选框,输出框里的字符从参与计算。档位写死在代码里:

熵值 标签 进度条宽度
< 40 弱 15%
40–59 一般 35%
60–79 强 65%
≥ 80 非常强 100%

页面现场读数:默认的 3 套 / 16 位显示 强度:非常强(熵值 95.3 位);加上符号是 103.6;加符号再勾排除反而降到 102.3,因为池从 89 缩到 84(16 × log2 84 = 102.28);只勾符号、16 位读 76.1,掉一档变「强」;四套全开加排除、长度设 4 读 25.6,「弱」。

正因为条和框各算各的,先点生成、再拖滑块,框里那串一个字没变而标签会重算:16 位生成完把滑块拖到 8,条会缩,框里仍是那 16 位。要复制的是框里的内容,不是条的读数。

检测器的分数不是熵

检测器顶部那个数字是 0–120 的分数,由一堆固定加分凑出来,熵值只是其中一项加分来源:

  • 长度到 8 加 15,到 12 再加 10,超过 12 的每一位另加,但封顶 8 分
  • 含小写、大写、数字各加 10,含非字母数字加 15
  • 熵值跨过 40 / 60 / 80 / 100 依次加 5 / 8 / 8 / 8
  • 用到三类字符加 5,四类齐全再加 5

标签由分数而不是熵决定:≤20 🔴 弱,≤40 🟡 较弱,≤60 🟡 中等,≤80 🟢 强,再往上 🟢 非常强。注意这套词和生成器那套(弱/一般/强/非常强)并不同名同界,两页写着同样的字时指的也不是同一个阈值。

现场取样:password → 25 分较弱;Abcdefghijk1 → 73 分强;Aa1!Aa1! → 75 分强;Tr0ub4dor&3 → 83 分非常强;24 个连续的 a → 72 分强。最后这条最能说明「按字符类别加分」这套办法看不见什么。

它的熵公式形状一样但池子模型不同:小写 26、大写 26、数字 10,而「特殊符号」一律按 32 计,判定式就是 [^a-zA-Z0-9]——只要不是 ASCII 字母数字就算落进这 32 个里。实测 密码测试 被判成 4 个特殊符号、其余类别全空,20.0 位;一个猫 emoji 的长度是 2,因为这页按 UTF-16 码元数长度,所以辅助平面的字符被数两次、得 10.0 位。换句话说,非拉丁文字口令在真实字母表规模上被低估,在「特殊符号」这一桶里又被高估,两个误差同时发生。页面上那六个勾选项严格对应 len8/lower/upper/number/special/len12,其中 len12 被标成加分项,它们只报「有没有」,不报别的。

常见密码表只有 40 条,而且是精确匹配

那条红色警告把你输入的内容转成小写,去比一张写死的表。表里是 40 条、互不重复:123456、password、12345678、qwerty、123456789、12345、1234、111111、1234567、sunshine、qwerty123、admin、letmein、password1、123123、iloveyou、monkey、dragon、master、666666、abc123、654321、123321、1234567890、welcome、princess、football、shadow、trustno1、superman、1qaz2wsx、qwertyuiop、123qwe、passw0rd、zaq1xsw2、000000、login、starwars、pass123、batman。

大小写被忽略,所以 PASSWORD 和 password 一样命中,提示语还会把你敲的原样回显出来:此密码出现在常见密码列表中(如 "PASSWORD"),极不安全!请勿使用。(用的是文本节点赋值,不是拼 HTML)。除了这张表以外没有任何模糊逻辑:P@ssw0rd 不在表里、静默通过;Aa1!Aa1! 是四个字符重复两遍,判「强」;Abcdefghijk1 是字母顺序串,判「强」。没有泄露库联网比对,也没有键盘行走检测。40 条是有用的下限,不是词库。

破解时间:模型和那个差十倍的单位

估算式是平均尝试 2^(熵−1) 次,在线按每秒 10 次、离线按每秒 1010 次换算;熵超过 1000,或结果超过 1e15 秒,直接显示 几乎不可能。换算单位在 1 秒、60、3600、86400、2592000(按 30 天计的月)、31536000(按 365 天计的年)、315360000 处切换,最后在 3.1536e12 秒处再切一次。

这些读数请当数量级看,别当日历。password 离线 10 秒、在线 331 年;Aa1!Aa1! 离线 4 天;Abcdefghijk1 离线 5115 年。页面自己写明了两条速率假设,这部分是诚实的:每秒 10 次假设你面对一个限速的登录口,每秒 100 亿次假设攻击者已经拿到哈希文件且那是个不加盐的快哈希。对着 bcrypt / Argon2,离线那一列会离谱地偏悲观,而这正是你希望它错的方向。

但最大那一档的单位是错的:它把秒数除以 3.1536e10——那是 1000 年,不是一个世纪——然后接上「世纪」两个字。于是 Abcdefghijkl1 显示 317 世纪,底层数字其实是 31.7 万年,约合 3170 个世纪,也就是说写出来的数比它自己的单位小了十倍。在这页看到「世纪」请自动读成「千年」;并且既然已经落到这一档,具体数字本身也没有现实意义了。

你输入的文字经历了什么

检测器的输入框是 type="password",带 autocomplete="off" 和 spellcheck="false",眼睛按钮只是把同一个框切成 type="text"——遮挡纯显示层,两种状态下分析都跑在原文上。生成器的输出框是只读纯文本输入框,结果永远亮在屏幕上,下面那 6 条批量项也是明文。

两页自身代码里没有任何 fetch、XMLHttpRequest、sendBeacon,两页对存储的唯一一次访问是读一个存看板猫模型偏好的 localStorage 键。工具不会把你的密码发出去。但两页都会加载第三方脚本(广告位和 CDN 上的看板猫),所以「页面不外发数据」是关于这两页逻辑的陈述,不是关于页面上每一个脚本。复制走异步 Clipboard API,失败时退回 execCommand;点批量项里任意一条时,✓ 已复制 的反馈出现在复制按钮上而不是你点的那一行——这是唯一可能让你以为没复制成功的地方。

顺序上怎么用

创建新凭据时用生成器:四套全开、长度 20 起步。89 的池取 20 位约 129.5 位,而长度是这页给你的唯一杠杆——字符池写死,16 位最多 103.6 位,想再高只能加长。除非你确实要手抄密码,否则排除相似不用勾,它拿掉 89 里的 5 个可能,只换来可读性。

不要用检测器去验一个真实密码强不强。要验的是形状:同长度、同类别组合、换掉具体字符。分数几乎完全由长度和类别存在性决定,所以忠实的假样本会得到同样的分数。真输入进去之后,屏幕上那两个数回答的是两个不同问题:分数在问「这串像不像机器生成的」,破解时间在问「如果这个假设错了会怎样」。

两页一起停在哪里

两页都不知道你的具体风险:没有词典、姓名、生日模式匹配,键盘行走只覆盖了表里已有的那两条,没有泄露库比对,也不会提醒你「别在两个站点用同一个」。生成器没有可念的词组模式、没有自定义符号、没有历史、也不能要求「至少两个符号」。而检测器那串 24 个 a 拿到 72 分,是这一整类工具的长期警告:只要强度是按 长度 × log2(字符池) 度量的,一串毫无熵的字符也可能被判成破不开。

本指南用到的工具

广告