一、先算一笔账
破解难度的本质是猜测空间,通常用「熵」(bit)衡量:
熵 ≈ 长度 × log2(字符集大小)
8 位小写字母 = 8 × 4.7 ≈ 38 bit
8 位大小写+数字 = 8 × 5.95 ≈ 48 bit
12 位小写字母 = 12 × 4.7 ≈ 56 bit
16 位小写字母 = 16 × 4.7 ≈ 75 bit
看起来「大小写+数字」赢了,但人选的复杂度是有规律的:Password1! 这种模式在字典+规则攻击下几乎瞬间被破,实际熵远低于理论值。而 16 位随机小写字母虽然「看着简单」,攻击者却真的要从 26¹⁶ 里穷举。
结论:长度带来的收益是线性的、可预测的;人为堆复杂度带来的收益常被攻击字典吃掉。
二、NIST 现在怎么说
美国 NIST 的密码指南(SP 800-63B)明确建议:
- 不要强制定期更换密码。频繁更换会促使用户用
Password1→Password2这种可预测的变体,反而更弱。只在「确认泄露」时才要求改。 - 不要限制特殊字符种类,也不要禁止长密码或粘贴(阻止粘贴会逼用户设短密码)。
- 检查是否在泄露库中(如 Have I Been Pwned),命中就拒绝。
- 支持长密码(至少 64 字符)与 Unicode。
- 必须加限流,防在线暴力破解。
也就是说,重点从「规则约束」转向了「长度 + 泄露检测 + 限流」。
三、实用建议:口令短语
对需要人记的密码,最划算的是口令短语:4–5 个随机常见词拼起来。
correct-horse-battery-staple
这类密码长度天然够(25+ 字符),熵约 44–55 bit 以上(取决于词表大小与词数),好记、好输,也不容易被规则攻击命中。缺点是部分老系统限制长度或不允许空格/连字符。
对不需要人记的(数据库、API key、服务账号),直接用随机生成的长字符串,32 位以上,交给密码管理器或密钥管理服务。
四、密码管理器是前提
人不可能记住几十个高强度密码,所以现实方案只有一个:一个强主密码 + 密码管理器,其余全部随机生成。这样做的额外好处是:
- 每个站点密码不同,一处泄露不会连锁。
- 可以放心用 20 位随机串,不用自己编。
- 钓鱼站域名不同,管理器不会自动填充,反而形成一道提醒。
风险是「主密码丢失 = 全部丢失」,所以务必做好恢复码/应急包备份。
五、服务端该怎么存
| 做法 | 评价 |
|---|---|
| 明文存 | 绝对不行 |
| MD5/SHA-1/SHA-256 单次哈希 | 不行,GPU 秒破 |
| 哈希 + 固定盐 | 不够,一破破一片 |
| bcrypt / scrypt / Argon2id + 每用户随机盐 | 正确 |
参数选择:bcrypt cost 10–12(约 100ms 一次),Argon2id 内存 64MB、迭代 3 次起步。原则是「让你的服务器算一次约 0.1 秒」,攻击者想批量猜就要付出同等代价。
另外两个常被忽略的点:
- 限流与锁定:再强的哈希也挡不住在线撞库,登录接口必须按 IP + 账号限速。
- 不要用「安全问题」做找回:母亲的姓氏、出生城市这类信息在社交网络上基本公开。
六、双因素比密码更重要
即使密码泄露,开了 TOTP 或硬件密钥也能挡住绝大多数账号接管。优先级上,给邮箱、云账号、代码仓库开二次验证的收益,远高于把密码从 12 位加到 16 位。具体原理见 TOTP 动态码。
七、快速生成与检查
需要一批随机密码时,用 密码生成器,可以指定长度、字符集、排除易混淆字符;要生成恢复码或 API key,用 随机生成器。生成在浏览器本地完成,不会把密码发到任何服务器——这一点在用在线工具时尤其重要。
小结
长度优先于复杂度;不要定期强制改密码,只在泄露时改;用密码管理器 + 随机密码;服务端用 bcrypt/Argon2id 加盐存储并限流;双因素优先级高于加长密码。