最后更新:
密码长度与安全性 - 如何选择最佳密码长度
密码安全性的最大决定因素是"长度"。短密码可以被暴力破解 (brute force) 瞬间攻破,而每增加一个字符,组合数就会增加约 95 倍。本文以 2025 年公开了第 4 版的 NIST SP 800-63B 为核心,从熵的概念、哈希函数的计算成本、暴力破解所需时间的量级、密码短语与多因素认证的组合等多个角度,深入剖析密码长度与安全性的关系。
熵与密码强度的关系
定量评估密码强度的指标是"熵"(信息量)。熵以比特为单位表示,值越大,攻击者越难以猜测。计算公式如下:
熵 (比特) = log2(字符种类数字符数) = 字符数 × log2(字符种类数)
例如,使用英文大小写字母 + 数字 + 符号共 95 种字符时,每个字符贡献约 6.57 比特的熵。8 个字符约 52.6 比特,12 个字符约 78.8 比特,16 个字符约 105.1 比特。
需要先说明一点:熵并不存在一条绝对的安全分界线。同样的比特数,面对每秒可尝试上千亿次的快速哈希,和面对刻意放慢速度的哈希,含义完全不同。熵只有与攻击者实际能达到的尝试速度相乘之后,才能换算成"大约需要多久才会被穷举"这一有意义的结论。因此更实用的理解方式是把熵当作一把相对刻度:比特数每增加 10,穷举所需的尝试次数就变为约 1024 倍。判断"够不够"时,应当同时看服务方使用的哈希算法、是否启用了多因素认证,以及该账户被攻破后的损失大小。
这里的关键在于,增加字符数比增加字符种类对熵的贡献更大。仅使用英文小写字母 (26 种) 的 16 个字符,熵约为 75.2 比特;而使用全部 95 种字符的 12 个字符,熵约为 78.8 比特,两者几乎相当。另一方面,即使仅使用英文小写字母,20 个字符的熵也能达到约 94 比特,超过 95 种字符 × 14 个字符 (约 92 比特)。"长度比复杂度更重要"这一说法的数学依据正在于此。
NIST SP 800-63B 指南要点
NIST SP 800-63B 的第 4 版于 2025 年 8 月公开,取代了此前的版本。围绕密码 (指南中称为"记忆式验证要素") 的字符数与策略,其要求可以整理如下。标注 SHALL 的项目为必须满足,标注 SHOULD 的项目为应当满足。
- 仅以密码作为唯一验证手段时:15 个字符以上 (SHALL)
- 密码作为多因素认证中的一个要素使用时:8 个字符以上 (SHALL)
- 最大字符数:至少应接受 64 个字符 (SHOULD)
- 强制字符种类的组成规则 (必须包含大写字母、符号等):不得施加 (SHALL NOT)
- 按固定期限强制更换密码:不得要求 (SHALL NOT)。仅在有证据表明该密码已经泄露时才要求更换
- 与已泄露密码清单的比对:必须实施 (SHALL)。比对对象是用户设置的密码整体,而不是其中的片段
- 熵的下限值:第 4 版没有提出任何要求。指南规定的是字符数、清单比对和存储方式,而不是让服务方去计算并判定熵值
把 15 个字符与 8 个字符分成两档,是因为这两种情形下密码承担的责任不同。密码是唯一防线时,它必须独自抵挡穷举和撞库;而在多因素认证中,密码只需守住"知识"这一层,另一层由持有物或生物特征承担,因此对字符数的要求可以放宽。
第 4 版之所以从"不推荐"进一步走到禁止强制组成规则,理由在于这类规则实际上并没有扩大攻击者需要搜索的范围。当规则要求"必须含有大写字母和符号"时,绝大多数用户会选择成本最低的应对方式,也就是把首字母改成大写、在末尾追加一个固定符号。攻击者早已把这些变形写进了破解规则集,于是规则带来的只是记忆负担,搜索空间几乎没有增加。同样的道理也适用于按期强制更换:被迫更换时用户倾向于只递增末尾的数字,攻击者从一个泄露的旧密码就能推出新密码。
NIST 重视"长度"而非"复杂度"的背景,除了前述熵的数学特性外,还有可用性研究的支持。研究证实,施加复杂规则会导致用户将密码写在便签上,或使用模式化的弱密码进行重复使用。
暴力破解所需时间的量级
密码字符数越多,暴力破解所需时间呈指数级增长。下表是一个计算例:字符集取英文大小写字母、数字和符号 (约 95 种),并假定攻击者每秒可以尝试 1011 次。这里的每秒次数不是某台具体显卡的实测值,而是为了看清量级而设定的前提,实际速度会随算法、硬件规模和攻击方式而大幅变动。
| 字符数 | 组合数 | 熵 | 穷举全部组合所需时间 (前提:每秒 1011 次) |
|---|---|---|---|
| 6 个字符 | 约 7350 亿 | 约 39.4 bit | 约 7 秒 |
| 8 个字符 | 约 6630 万亿 (6.63 × 1015) | 约 52.6 bit | 约 18 小时 |
| 10 个字符 | 约 6.0 × 1019 | 约 65.7 bit | 约 19 年 |
| 12 个字符 | 约 5.4 × 1023 | 约 78.8 bit | 约 1.7 × 105 年 |
| 16 个字符 | 约 4.4 × 1031 | 约 105.1 bit | 约 1.4 × 1013 年 |
阅读这张表时有两点必须注意。第一,它算的是穷举全部组合所需的时间,前提是密码为真正随机生成;如果密码源自单词、人名或键盘上的连续按键,攻击者根本不必逐个穷举,所需时间会缩短到与表中数值完全不同的量级。第二,前提中的每秒次数一旦改变,整张表就会同比例平移:把速度提高到 1000 倍,全部时间就缩短为千分之一。这也说明服务方选择的哈希算法会直接决定这张表的横轴:刻意放慢的哈希把每秒可尝试次数压低几个数量级,所需时间就随之延长同样的数量级。不过,由于服务器端的哈希算法不由用户选择,用户能做的最佳防护就是"设置足够长的密码"。
哈希函数的计算成本与字符数的关系
密码的安全性不仅取决于字符数,还很大程度上依赖于服务器端使用的哈希函数的计算成本。以下是主要哈希算法的特性比较:
| 算法 | 在密码存储中的定位 |
|---|---|
| MD5 | 为求快而设计的通用哈希,速度过快,不适合用于密码存储。目前主要残留于遗留系统中 |
| SHA-256 | 比 MD5 更结实,但同样是以高速为目标的通用哈希,直接用于密码存储仍然不足 |
| bcrypt (cost=12) | 专为密码存储设计,故意放慢速度,并可通过成本因子调整计算量。输入限制 72 字节 |
| Argon2id | 2015 年密码哈希竞赛的获选算法。除计算量外还要求大量内存,因此对 GPU 的大规模并行攻击抵抗力更强。RFC 9106 将其列为首选 |
| scrypt | 同样采用内存密集型设计,是 Argon2 出现之前的推荐算法。现有系统继续使用是合理的,新建系统则优先考虑 Argon2id |
bcrypt 有将输入截断为 72 字节的规范,因此仅使用 ASCII 字符时实际上限为 72 个字符,UTF-8 编码的中文约为 24 个字符。这影响了部分服务的最大字符数限制。Argon2id 没有此限制,可以处理任意长度的密码。新服务的设计推荐采用 Argon2id。
需要注意的是,即使使用 bcrypt 或 Argon2id 等慢速哈希,短密码仍可能被字典攻击或基于规则的攻击所攻破。哈希的计算成本只是"争取时间",根本性的防御在于确保足够的字符数 (熵)。
为什么"长度"比"复杂度"更重要
许多服务要求"必须包含大写字母、小写字母、数字和符号",但 NIST 明确否定了这种做法。以下用具体数据说明原因。
"P@ssw0rd!" 是 9 个字符,包含了全部 4 种字符类型,却是字典攻击排行榜上的典型弱密码。原因在于它是把 "password" 按 a→@、o→0 这类固定替换改写而成的,而攻击者的规则集早已把这些替换全部收录,因此这些替换几乎没有扩大需要搜索的范围。换句话说,l33t 替换增加的是记忆和输入的麻烦,而不是强度。
与之相对,"mountain river cloud forest" 这种从一份 7776 个词的词表 (Diceware 使用的规模) 中随机抽出 4 个词的写法,熵约为 51.7 比特;抽 6 个词则约为 77.5 比特。它看上去只是几个普通的小写单词,强度却来自"随机抽取了几次"这一点,而不是字符种类的多少。关键在于单词必须由随机方式选出:如果是自己想出来的、彼此有关联的词,可选范围会远小于词表规模,熵也就无从谈起。
复杂度规则适得其反的原因有三个。第一,用户会用可预测的模式来满足要求 (首字母大写,末尾添加 "1!" 等)。第二,复杂密码难以记忆,因此往往偏短。第三,无法记忆的密码更容易被以明文形式写在便签或文本文件中。
密码短语 vs 随机字符串
实现长密码有两种方法:"密码短语"和"随机字符串"。以下比较两者的特性:
| 比较项 | 密码短语 | 随机字符串 |
|---|---|---|
| 示例 | correct horse battery staple | kX9#mP2$vL7@nQ4 |
| 字符数 | 28 个字符 | 15 个字符 |
| 熵 | 约 51.7 bit (7776 个词的词表中抽 4 个词) | 约 98.5 bit (95 种 × 15 个字符) |
| 易记性 | 高 (可通过故事记忆) | 低 (需要密码管理器) |
| 输入便捷性 | 高 (普通打字即可) | 低 (特殊符号输入繁琐) |
| 字典攻击抵抗力 | 取决于单词数量和词表大小 | 极高 |
这张表里最值得注意的一行是熵。左侧的密码短语有 28 个字符,右侧的随机字符串只有 15 个字符,但熵却是右侧接近左侧的两倍。也就是说,用字符数来评估密码短语的强度,会大幅高估它的实际强度:密码短语的一个"单位"是一个单词而不是一个字符,因此字符数多并不等于抽取次数多。
密码短语的强度取决于"单词数量"和"词表大小"。使用 Diceware 方式 (7776 个词的词表) 时,4 个词约为 51.7 比特,6 个词约为 77.5 比特,每多抽一个词就增加约 12.9 比特。如果仅使用日常常用词,容易被字典攻击攻破,因此随机组合不相关的单词至关重要。"我的猫很可爱"这样有意义的句子不适合作为密码短语,"椅子 紫色 潜水艇 辣椒 银河"这样不相关单词的组合才是理想的。
主密码 (用于解锁密码管理器) 适合使用密码短语,而各个服务的密码则最好使用密码管理器生成的随机字符串。
如何面对服务方的字数上限
决定了自己想用多长的密码之后,下一个问题是服务方是否允许。麻烦的是,各家服务的最大字符数很少写在帮助页面里,写出来的也可能与实际实现不一致,而且会随着改版而变化。因此,与其去找一份"各服务上限一览",不如把它当作需要在自己的账户上确认的事项。
上限之所以存在,有两个常见的技术原因。一是哈希算法本身的规范,例如前面提到的 bcrypt 会把输入截断到 72 字节,超出部分不参与计算,于是服务方索性在输入阶段就设一个上限。二是数据库设计的历史遗留:在把密码或其派生值存进固定长度字段的年代,字段长度直接成了字符数上限,即使后来改用了现代的哈希,前端的校验规则也常常留在原处。反过来说,上限偏短本身并不一定意味着存储方式不安全,但它确实限制了用户能拿到的熵。
| 要做的事 | 具体做法 | 注意点 |
|---|---|---|
| 确认实际上限 | 用密码管理器生成 32 个字符的密码尝试保存,被拒绝时依次降到 24 个、16 个字符 | 有的服务不报错而是静默截断,保存后应立即退出并用完整密码重新登录以确认 |
| 记录确认结果 | 把该服务实际接受的最长长度记在密码管理器的备注栏里 | 改版后上限可能变化,下次更换密码时重新确认一遍 |
| 应对字符种类限制 | 若符号被拒绝,就改用仅由大小写字母和数字构成、但更长的密码 | 字符种类减少造成的熵损失,可以通过增加字符数补回来 |
| 上限偏短时 | 在允许范围内取最长,同时为该账户启用多因素认证 | 上限无法由用户改变,只能在其他层面补上防御 |
| 最小字数很宽松时 | 不把服务要求的下限当作目标,长度由自己决定 | 下限是面向全体用户的底线,不是推荐值 |
| 被强制要求字符种类 | 先把密码加长,再补足所缺的字符种类 | 用首字母大写、末尾加固定符号这类最小变形去应付,对搜索空间几乎没有贡献 |
| 被强制定期更换 | 整体重新生成,而不是在原密码上做改动 | 递增末尾数字的做法,会让攻击者从旧密码直接推出新密码 |
| 主密码 | 它不受任何服务的上限约束,可以取到自己能记住的最长长度 | 主密码只靠记忆管理,因此长度要与可记忆性取平衡 |
把这些整理成一条原则:在服务允许的范围内尽可能取长,上限短到无法取得足够熵的服务则必须靠多因素认证来补足;而在下限宽松的服务上,不要因为"8 个字符就能通过"而真的只设 8 个字符。字符数是少数几个完全由用户自己掌握的安全参数,把它交给服务方的校验规则来决定并不划算。想核对自己设定的长度时,可以用字符计数器确认。
多因素认证的组合
无论密码多长,一旦通过钓鱼或键盘记录器被窃取就毫无用处。密码"长度"的防御与多因素认证 (MFA) 的防御针对不同的攻击向量,组合使用可以大幅提升防御能力。
- TOTP (基于时间的一次性密码):Google Authenticator 或 Authy 等应用生成的 6 位数代码。每 30 秒更新一次,即使被窃取也难以重复使用。但对实时中继的钓鱼攻击 (实时钓鱼) 存在脆弱性
- FIDO2 / WebAuthn (通行密钥):使用物理安全密钥或设备的生物识别进行认证。它把连接对象的信息 (域名等) 绑定到密钥上,在认证时一并核对,因此即使用户被引导到外观相同的假网站,密钥也不会对该网站作出响应,钓鱼在原理上就被挡住了。SP 800-63B 第 4 版同样把这类方式归入具有抗钓鱼能力的手段。Apple、Google、Microsoft 正以"通行密钥"的名义积极推广
- 短信认证:方便但存在 SIM 卡交换攻击和 SS7 协议漏洞导致的拦截风险。SP 800-63B 第 4 版把经由公共电话网 (PSTN) 的带外认证明确列为"受限"(RESTRICTED) 的手段,建议尽可能迁移到 TOTP 或 FIDO2
理想的配置是"长密码 (或密码短语) + FIDO2 安全密钥"。这样可以同时对暴力破解、字典攻击、钓鱼和凭证填充攻击提供高度抵抗力。
密码管理器的最佳设置
- 主密码设置:用于解锁密码管理器的主密码,应使用 Diceware 方式设置 6 个词以上的密码短语,6 个词对应的熵约为 77.5 比特,每多抽一个词再增加约 12.9 比特。主密码不受任何服务的上限约束,因此可以在自己能记住的范围内多抽几个词;它不应保存在任何地方,仅靠记忆管理
- 自动生成密码的长度:各服务的密码应自动生成 20 个字符以上的随机字符串。考虑到 bcrypt 的 72 字节限制,仅使用 ASCII 字符时 20-64 个字符是实用范围
- 剪贴板自动清除:启用复制的密码在一定时间后自动删除的设置。推荐 10-30 秒
- 与浏览器内置密码保存功能的区分:Chrome 和 Safari 的内置密码管理器也具备充分的加密功能,但如果需要跨平台使用或高级功能 (安全审计、共享保险库等),专用工具更为合适
泄露检查与密码策略设计指南
以下是面向个人用户和服务开发者的实践指南。
面向个人用户:
- 定期在 haveibeenpwned.com 检查自己的邮箱是否包含在过去的泄露中。该网站的 API 采用 k-匿名性模型,仅发送密码哈希值的前 5 个字符,密码本身不会被传输到外部
- 利用密码管理器的"安全审计"功能,一次性检测弱密码、重复使用和已泄露的密码
- 优先加强重要账户 (邮箱、金融、社交媒体) 的密码。邮箱账户因被用于其他服务的密码重置,应最优先保护
面向服务开发者:
- 把最小字符数分成两档:密码是唯一验证手段时要求 15 个字符以上,作为多因素认证中的一个要素时要求 8 个字符以上
- 最大字符数应允许 64 个字符以上。不必要的短上限会限制用户的安全性
- 不设置字符种类强制规则,也不按期限强制更换。取而代之,实现与泄露密码列表 (如 Have I Been Pwned 的 API) 的比对,并且比对用户提交的密码整体
- 哈希算法首选 Argon2id,次选 bcrypt (成本因子 12 以上)。绝对避免直接使用 MD5 或 SHA-256
- 实现密码强度计,为用户提供实时反馈。比起只统计字符种类是否齐全,更理想的是 zxcvbn 这类结合词表、已知模式和键盘排列来推算"需要多少次尝试才能猜中"的方式。只看字符种类的强度计会把 "P@ssw0rd" 这种词典派生的密码评为高分,反而误导用户
常见错误模式与对策
- 直接使用字典中的单词:"sunshine"、"football"、"dragon" 等常见英文单词会被字典攻击瞬间攻破。攻击者除了使用数百万词的字典外,还会全面尝试 l33t speak 转换 (a→@、e→3 等) 和键盘模式 (qwerty、1qaz2wsx 等)
- 在密码中包含个人信息:生日、宠物名字、地址的一部分等,可以从社交媒体的公开信息或数据泄露中轻易推测。攻击者日常使用收集目标公开信息来创建自定义字典的手法 (社会工程学)
- 在多个服务中重复使用同一密码:一个服务泄露后,使用相同密码的所有服务都会面临风险。这种被称为"凭证填充"的攻击,会把泄露的邮箱与密码组合自动拿到其他服务上逐个尝试。要注意的是,这种攻击与密码的长度完全无关:攻击者手里已经有了正确答案,无论密码是 40 个字符还是 8 个字符,第一次尝试就会成功。加长密码对它没有任何防御作用,唯一的办法是不重复使用,也就是每个服务一个独立密码,并对重要账户启用多因素认证
- 对密码进行微小改动后重复使用:"MyPassword1"、"MyPassword2"、"MyPassword3" 这样的模式,攻击者的基于规则的攻击可以轻易推测。从一个泄露的变体生成其他变体的工具已广泛流传
总结
密码的安全性取决于熵 (信息量),而最有效提高熵的手段是增加字符数。NIST SP 800-63B 第 4 版把最小字符数分成两档:密码是唯一验证手段时要求 15 个字符以上,作为多因素认证中的一个要素时要求 8 个字符以上;同时禁止强制组成规则和按期限强制更换。服务方再结合 bcrypt 或 Argon2id 等慢速哈希,就能把用户设定的字符数真正兑换成安全性。个人用户应使用密码管理器生成 20 个字符以上的随机密码,主密码使用 Diceware 密码短语。再配合 FIDO2 / 通行密钥的多因素认证,即可实现当前最高水平的防御。想要确认密码字符数时,请使用字符计数器。