密码强度与熵:从随机数到强密码的完整指南

为什么”8 位含大小写数字符号”还不够

你大概率见过这类密码建议:

  • 至少 8 位,包含大小写 + 数字 + 符号
  • 每 90 天更换一次
  • 不要使用上次密码的变体

但很少人告诉你:8 位四类字符集的密码,熵约 51.9 bits,现代 GPU 暴力破解只需数小时

更糟的是,强制更换促使用户选择 Password1!Password2! 这种最小变体,实际熵几乎没有增加。

本文从熵的本质出发,讲清楚什么是真正的”强密码”,以及如何用现代工具生成符合安全要求的密码。

配套工具:密码生成器

一、熵:密码强度的唯一标尺

1.1 香农熵公式

密码强度的本质是熵(entropy),衡量攻击者需要尝试多少次才能暴力破解。香农熵公式:

熵(bits) = 长度 × log₂(字符集大小)
  • 字符集大小:小写 26、大写 26、数字 10、常见符号约 22-32
  • 长度:密码字符数

1.2 单字符熵对比

字符集字符数单字符熵
仅数字103.32 bits
仅小写字母264.70 bits
仅小写 + 数字365.17 bits
大小写 + 数字625.95 bits
大小写 + 数字 + 符号~906.49 bits

1.3 长度比字符集更重要

这是一个反直觉但关键的结论。对比两个密码:

  • Aa1! 重复 2 次 = 8 位四类字符集:熵 ≈ 51.9 bits
  • correcthorsebatterystaple = 25 位仅小写字母:熵 ≈ 117.5 bits

后者熵是前者的 2.3 倍,且更易记忆。这就是为什么 NIST SP 800-63B 不再强制要求复杂字符集,而是鼓励长密码或密码短语(passphrase)

1.4 长度推荐阈值

熵阈值含义推荐长度(四类字符集)
28 bits在线攻击可破解≥ 5 位
36 bits离线暴力数分钟≥ 6 位
60 bits离线 GPU 数年≥ 10 位
100 bits离线不可行≥ 16 位
128 bits抵御国家级攻击≥ 20 位

建议:日常账户密码 ≥ 16 位(熵 ≥ 100 bits),加密密钥 ≥ 20 位(熵 ≥ 128 bits)。

二、CSPRNG vs PRNG:随机数的安全等级

2.1 PRNG(伪随机数生成器)

Math.random 是典型的 PRNG,基于确定性算法(如 xorshift128+)生成”看起来随机”的序列。

状态 = 算法(状态) → 输出

问题:状态可被重建。攻击者收集足够输出(约 2-3 次 64 位输出)即可反推内部状态,预测后续所有”随机数”。这攻击在浏览器 PRNG 上已被实证。

2.2 CSPRNG(加密安全伪随机数生成器)

crypto.getRandomValues 是 CSPRNG,基于操作系统熵池(Linux /dev/urandom、Windows CryptGenRandom)。

特性

  • 即使内部状态泄露,也无法预测后续输出
  • 满足”下一比特不可预测性”(next-bit test)
  • 满足”状态不可回推”(backtracking resistance)

使用场景:会话令牌、API 密钥、密码、加密密钥、CSRF token、密码重置链接。

2.3 浏览器中的 CSPRNG API

API用途输出
crypto.getRandomValues(Uint8Array)任意长度随机字节Uint8Array
crypto.randomUUID()UUID v4字符串(推荐用于 UUID 生成
crypto.subtle.generateKey()密钥对生成CryptoKey

注意:所有 CSPRNG 都需在安全上下文(HTTPS 或 localhost)下使用,部分浏览器在 HTTP 下会限制 crypto.subtle

三、模偏差与拒绝采样

3.1 什么是模偏差

最朴素的密码生成实现:

const charset = 'abc...XYZ012!@#';
const idx = Math.floor(Math.random() * 256) % charset.length;
return charset[idx];

charset.length 不整除 256 时,前面的字符出现概率更高。例如:

  • charset.length = 90(四类字符集)
  • 256 / 90 = 2 余 76
  • 前 76 个字符出现概率 = 3/256 ≈ 1.17%
  • 后 14 个字符出现概率 = 2/256 ≈ 0.78%
  • 偏差比例:前 76 个字符比后 14 个高约 50%

3.2 拒绝采样(rejection sampling)

正确做法是丢弃落入”非均匀区间”的随机字节:

function pickChar(charset) {
  const len = charset.length;
  // 计算均匀分布的区间上限
  const limit = Math.floor(256 / len) * len;
  const buf = new Uint8Array(1);
  while (true) {
    crypto.getRandomValues(buf);
    if (buf[0] < limit) {
      return charset[buf[0] % len];
    }
    // 否则丢弃,重新取
  }
}

3.3 重试概率分析

charset.lengthlimit丢弃区间重试概率
10(仅数字)250250-2552.3%
26(小写字母)234234-2558.6%
62(大小写+数字)248248-2553.1%
90(四类+符号)180180-25529.4%

最坏情况约 30% 重试概率,平均重试次数 = 1 / (1 - 0.294) ≈ 1.42 次,性能完全可接受。

3.4 优化:批量取字节

对 90 字符集场景,可一次取 8 字节批量处理,减少 crypto.getRandomValues 调用次数。本工具暂未实现此优化,单次生成 ≤ 128 字符密码性能已足够。

四、字符集策略

4.1 字符集选型矩阵

场景推荐字符集长度
网站账户密码大小写 + 数字 + 符号16~104 bits
密码短语(易记)仅小写字母25~117 bits
API 令牌大小写 + 数字32~190 bits
WiFi 密码大小写 + 数字(排除易混)16~94 bits
加密密钥大小写 + 数字 + 符号32~208 bits
一次性口令仅数字6~20 bits

4.2 排除易混字符

视觉相近字符在常见字体下易混淆:

字符易混对象字体场景
0O / o无衬线字体
1l(小写 L)/ I(大写 i)等宽字体
``l / 1 / I
'"终端字体
`'终端字体

何时勾选

  • 需打印、手抄、口头传达的密码(WiFi、会议口令、应急密码)
  • 客服系统中口头告知的临时密码

何时不必勾选

  • 密码管理器自动填充
  • 服务端生成、客户端存储的令牌

4.3 符号集设计

本工具符号集为 !@#$%^&*()-_=+[]{};:,.?/,避开以下字符:

字符避开原因
空格粘贴时易截断
" 'SQL/Shell/HTML 注入风险
\Shell/JSON/正则转义字符
< >HTML 标签边界
`Shell 命令替换

这些字符在某些表单中会被拒绝或需特殊转义,避开它们能提升兼容性。

五、强度评估的等级划分

5.1 等级与熵区间

本工具基于熵值(bits)划分 5 个等级:

等级熵区间颜色含义
极弱< 28秒级破解
28-36分钟级破解
一般36-60本地数小时-数天
60-100绿在线不可行,离线数年
很强≥ 100深绿离线也不可行

5.2 阈值依据

  • 28 bits:在线攻击每秒约 1000 次尝试,28 bits = 2^28 / 1000 / 3600 ≈ 74 小时,分钟到小时级
  • 60 bits:现代 GPU 暴力每秒约 10^10 次(如 hashcat + RTX 4090),60 bits = 2^60 / 10^10 / 86400 ≈ 13 天
  • 100 bits:分布式 GPU 集群(10^12 次/秒)需 2^100 / 10^12 / 86400 / 365 ≈ 4 × 10^10 年
  • 128 bits:理论上不可暴力破解,热力学极限(按 Landauer 原理,遍历 2^128 种状态所需能量超过地球寿命)

5.3 与 zxcvbn 等工具的差异

zxcvbn 等工具评估的是人类记忆密码的实际熵,会识别:

  • 字典词(password 实际熵远低于 4.7 × 8 = 37.6 bits)
  • 日期、姓名、键盘模式
  • 替换字符模式(p@ssw0rdpassword 实际熵接近)

本工具评估的是理论熵(基于 CSPRNG 生成的最大熵),适用于生成密码而非评估用户输入密码。两者结合使用更佳。

六、记忆密码 vs 密码管理器

6.1 记忆密码(passphrase)

适合主密码(密码管理器主密码、设备解锁密码)。

DiceWare 方法:从 7776 个常见词的词典中随机选 5-7 个词拼接,每词熵约 12.9 bits。

correct horse battery staple yahoo
→ 6 词 × 12.9 = 77.4 bits

优点

  • 易记忆
  • 熵足够高
  • 可手写输入

缺点

  • 词典泄露后熵下降
  • 长度较长(30+ 字符)

6.2 密码管理器

适合日常账户密码。每个账户使用独立随机密码,由密码管理器统一存储。

推荐流程

  1. 主密码使用 passphrase(≥ 6 词,熵 ≥ 77 bits)
  2. 每个账户使用 密码生成器 生成 16 位四类字符集密码(熵 ≈ 104 bits)
  3. 开启 2FA / MFA 二次验证
  4. 定期检查 Have I Been Pwned 是否泄露

密码管理器推荐

  • 开源自托管:Bitwarden / Vaultwarden
  • 商业服务:1Password、Dashlane
  • 本地存储:KeePassXC

6.3 NIST SP 800-63B 现代建议

NIST 在 2017 年发布的 SP 800-63B 附录 A 中明确建议:

  • 最小长度 8 位(用户密码)/ 6 位(PIN)
  • 鼓励长密码(≥ 64 位允许)
  • 允许所有 ASCII 字符(含空格)
  • 不强制复杂字符集(不要求数字+符号+大小写)
  • 不强制定期更换(除非已泄露)
  • 拒绝常见弱密码(top 1000 弱密码字典)
  • 比对泄露数据库(Have I Been Pwned API)

七、工具矩阵联动

本工具与工具盒子其他工具形成「安全」主题矩阵:

工具联动场景
密码生成器生成强密码
UUID 生成器生成会话 ID、API 令牌
Hash 计算密码哈希摘要(避免明文存储)
Base64 编解码令牌编码传输
JWT 解码解析含密码的认证令牌
URL 编解码密码重置链接参数

7.1 密码生成 + 哈希存储(服务端场景)

# 1. 用密码生成器生成用户初始密码
密码 = "K9#aB3$xY7nQ2mP!"  # 16 位四类字符集,熵 ≈ 104 bits

# 2. 服务端用 SHA-256 + salt 哈希存储
echo -n "K9#aB3$xY7nQ2mP!" | sha256sum
# → 3a7d...(用 [Hash 计算](/hash) 工具验证)

# 3. 实际生产用 bcrypt / argon2 而非 SHA-256(防暴力)

7.2 密码生成 + UUID 令牌(API 场景)

# 1. 用户密码(用户登录用)
密码 = "K9#aB3$xY7nQ2mP!"

# 2. API 令牌(程序调用用)
token = "550e8400-e29b-41d4-a716-446655440000"  # UUID v4,用 [UUID 生成器](/uuid) 生成

# 3. 令牌 Base64 编码(HTTP 头传输)
echo -n "550e8400-e29b-41d4-a716-446655440000" | base64
# → NTUw...(用 [Base64 编解码](/base64) 工具验证)

7.3 密码重置链接(URL 场景)

# 1. 生成一次性重置令牌
token = "K9#aB3$xY7nQ2mP!"  # 16 位四类字符集

# 2. URL 编码(处理特殊字符)
# 用 [URL 编解码](/url) 工具编码
encoded = "K9%23aB3%24xY7nQ2mP%21"

# 3. 拼接重置链接
link = "https://example.com/reset?token=" + encoded

八、扫描兼容性自检清单

生成密码后,按以下清单验证可用性:

  • 长度 ≥ 16 位(或熵 ≥ 100 bits)
  • 字符集至少包含 3 类(小写 + 大写 + 数字)
  • 符号集与目标系统兼容(避开空格、引号、<> \
  • 排除易混字符(需手抄 / 口头传达时)
  • 不含个人信息(生日、姓名、电话)
  • 不在 Have I Been Pwned 数据库中
  • 每个网站使用独立密码
  • 已开启 2FA / MFA 二次验证
  • 已备份到密码管理器

总结

强密码的本质是,不是”看起来复杂”。三个核心要点:

  1. 长度 > 字符集:16 位仅小写字母的熵(75.2 bits)高于 8 位四类字符集(51.9 bits)
  2. CSPRNG > PRNG:密码生成必须用 crypto.getRandomValues,不能用 Math.random
  3. 拒绝采样消除模偏差:简单 % charsetSize 在字符集大小不整除 256 时会引入概率偏差

实践层面:日常密码用 16 位四类字符集(熵 ≈ 104 bits),密码管理器主密码用 6 词 passphrase(熵 ≈ 77 bits),加密密钥用 32 位四类字符集(熵 ≈ 208 bits)。配合 UUID 生成器Hash 计算Base64 编解码 等工具,覆盖从生成、存储、传输到验证的完整安全链路。