Skip to content
返回博客
安全

密码熵详解:如何计算和提升密码强度

掌握密码熵:计算公式、暴力破解时间估算,以及为什么长度比复杂度更重要。含 JS/Python 代码示例和免费密码生成器。

10 min read

密码熵:如何衡量和最大化你的密码强度

你可能听过”强密码”要包含大写字母、数字和特殊字符。但 P@$$w0rd! 满足所有这些规则,却能在不到一秒内被破解。

密码强度的真正衡量标准不是你用了什么字符,而是——一个来自信息论的概念,用来量化密码的不可预测性。

本文将详解密码熵的工作原理、计算方法,以及如何生成真正难以破解的密码。

什么是密码熵?

密码熵衡量的是密码的不可预测程度,以「比特」(bit)为单位。每增加一个比特的熵,攻击者需要尝试的次数就翻一倍。

可以用骰子来类比:一个 6 面骰子每次投掷有约 2.6 比特的熵——只有 6 种可能。一个 20 面骰子有约 4.3 比特——面越多,不确定性越大。

密码的原理相同:可选字符越多(骰子面数越大)、密码越长(投掷次数越多),熵就越高。

这就是为什么熵比复杂度规则更科学。

一个看起来很复杂的密码(Tr0ub4dor&3)可能熵很低,因为它遵循了可预测的模式。而一个看起来简单的密码短语(correct horse battery staple)反而有很高的熵,因为它从一个巨大的词池中随机选取。

计算公式:如何计算密码熵

公式很简单:

E = L × log₂(R)

其中:

  • E = 熵(比特)
  • L = 密码长度(字符数)
  • R = 字符池大小(每个位置可能的字符数)

字符池大小对照表

字符类型池大小 (R)每字符熵(比特)
纯小写字母 (a-z)264.70
小写 + 数字365.17
大小写 + 数字625.95
全部可打印 ASCII946.55
Diceware 词表7,776每词 12.92

用代码计算

// 用 JavaScript 计算密码熵
const entropy = (length, poolSize) =>
  length * Math.log2(poolSize);

entropy(8, 26);   // → 37.60 比特(纯小写)
entropy(12, 62);  // → 71.45 比特(大小写+数字)
entropy(16, 94);  // → 104.87 比特(全字符集)
import math

def entropy(length: int, pool_size: int) -> float:
    return length * math.log2(pool_size)

entropy(8, 26)   # → 37.60 比特
entropy(12, 62)  # → 71.45 比特
entropy(16, 94)  # → 104.87 比特

注意:这个公式假设每个字符是均匀随机选择的。如果是人为选择的密码(使用常见单词或模式),实际熵远低于理论值。

多少熵才够安全?

答案取决于你保护的是什么,以及攻击者的猜测速度。

现代 GPU 对 MD5 等快速算法每秒可测试超过 10¹²(一万亿)个哈希。以下是不同熵值的实际意义:

熵值(比特)强度破解时间(10¹² 次/秒)建议用途
< 40不到 1 秒不建议使用
40–59一般秒到小时临时账号
60–79天到数百年普通账号
80–99很强千年以上邮箱、银行
100+极强超过宇宙热寂时间加密密钥、主密码

使用全部可打印 ASCII 字符的 16 位密码有约 105 比特的熵——已经达到「极强」级别。你可以用我们的随机密码生成器即时生成,工具会实时显示每个密码的熵值分析。

NIST 新指南(2024 更新)

美国国家标准与技术研究院(NIST)SP 800-63B 在 2024 年做了重大更新:

  • 取消了强制复杂度规则(不再要求必须包含特殊字符)
  • 取消了强制定期更换密码
  • 提高最低密码长度至 15 个字符(原为 8 个)
  • 强调筛查已泄露的密码
  • 鼓励长度和随机性优先于复杂度

这些变化印证了熵的数学原理一直在告诉我们的:长度和随机性比字符种类更重要

为什么长度比复杂度更重要

来看一下数学。考虑两种提升 12 位密码熵的方式:

方案 A —— 保持 12 位,从大小写+数字(62)换成全 ASCII(94):

  • 12 × log₂(94) - 12 × log₂(62) = 78.66 - 71.45 = +7.21 比特

方案 B —— 保持大小写+数字(62),多加一个字符(12 → 13):

  • 13 × log₂(62) - 12 × log₂(62) = 77.40 - 71.45 = +5.95 比特

增加一个字符带来的熵提升,几乎和切换到更大字符集一样多。加两个字符就超过了。

再看 P@$$w0rd!(9 个字符)。它虽然使用了全 ASCII 字符集,但太短了。更糟的是,它遵循了可预测的「leet speak」模式,字典攻击早已覆盖,所以实际熵远低于理论值 59 比特。

结论:对于真正随机的密码,增加长度比增加字符种类更高效。但真正的敌人是可预测性,不是长度不够。

密码短语 vs 随机密码

维度随机密码密码短语(Diceware)
示例kX#9mP$2vL!nQ7wRcorrect horse battery staple
每单位熵6.55 比特/字符12.92 比特/词
达到约 78 比特需要12 个字符6 个词
可记忆性
手机输入困难方便
最适合密码管理器中的密码主密码、需要记忆的登录

Diceware 的工作原理

Diceware 使用一个包含 7,776 个词的词表(6⁵ = 7,776)。每个词通过掷五次骰子选出,提供恰好 12.92 比特的熵。

四个词约 51 比特;六个词约 77 比特。

该选哪个?

  • 存在密码管理器里的密码:使用 16 位以上、全字符集的随机密码。你不需要手动输入,所以记忆性不重要。我们的随机密码生成器可以一次批量生成最多 50 个。
  • 主密码:使用 5-6 个词的 Diceware 密码短语。它足够好记,又能提供 64-77 比特的熵。
  • API 密钥和令牌:使用 openssl randcrypto.randomBytes() 获取最大熵值,不需要考虑人类记忆。

实战:开发者工具与代码

以下是开发者生成高熵密钥的常用方式:

浏览器(Web Crypto API)

// 密码学安全的密码生成
function generatePassword(length, charset) {
  const array = new Uint32Array(length);
  crypto.getRandomValues(array);
  return Array.from(array, v => charset[v % charset.length]).join('');
}

const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*';
generatePassword(16, chars);
// → 'kX#9mP$2vL!nQ7wR'(每次随机)

Node.js

const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('base64url');
// → 'Ql2Hj8xK9mNp3rVw5tYz7uBa0cEf4gIk'(43 字符,256 比特)

Python

import secrets
token = secrets.token_urlsafe(32)  # 256 比特的熵
password = secrets.token_hex(16)    # 128 比特,十六进制格式

命令行

# 192 比特的熵,base64 编码
openssl rand -base64 24

# 256 比特,十六进制编码
openssl rand -hex 32

各方法熵值对比

方法输出长度熵(比特)
UUID v436 字符122
openssl rand -base64 2432 字符192
16 位全 ASCII16 字符105
6 词 Diceware约 30 字符78
4 词 Diceware约 20 字符52

永远不要用 Math.random() 做任何安全相关的事。它使用的是非密码学安全的伪随机数生成器——如果攻击者知道种子,输出就是可预测的。浏览器中请用 crypto.getRandomValues(),Node.js 中请用 crypto.randomBytes()

密码存储:为什么仅有熵还不够

即使是 128 比特的密码,如果服务端以 MD5 明文哈希存储,也形同虚设。数据库泄露后,攻击者在单张 GPU 上每秒就能测试数十亿个 MD5 哈希。

这就是慢哈希算法的用武之地。它们的设计目标就是让每次猜测都变得昂贵:

算法GPU 上的速度等效减速
MD5约 100 亿次/秒基准线(不要使用)
SHA-256约 50 亿次/秒约 2 倍
bcrypt (cost=12)约 5 次/秒约 20 亿倍
argon2id约 2 次/秒约 50 亿倍

bcrypt 的 cost 参数设计得很优雅:每增加 1,所需计算量翻倍。cost=12 意味着 2¹² = 4,096 轮哈希运算,相当于在密码本身的熵之上额外增加了 12 比特的「存储熵」。

双重保护模型:高熵密码防御离线暴力破解,慢哈希防御数据库泄露。两者缺一不可。

想深入了解哈希算法的差异,请阅读 MD5 与 SHA-256 对比,也可以试试 MD5 哈希生成器来直观感受不同算法的输出。

常见密码迷思破除

”每 90 天换一次密码”

NIST 2024 版指南明确建议不要强制定期更换密码。频繁更换会导致用户选择更弱、更可预测的密码——在末尾加数字、在几个密码间轮换。除非有证据表明密码已泄露,否则不需要更换。

“a→@、e→3 能增强安全性”

Leet speak 替换是字典攻击最先尝试的模式之一。在 password 中把 a 替换成 @ 几乎不增加熵,因为攻击者早已预料到这种替换。

真正的随机性——而非巧妙的替换——才是提升熵的关键。

“8 位加特殊字符就够了”

即使用了全部 94 个 ASCII 字符,8 位密码也只有 52 比特的熵。以每秒 10¹² 次的猜测速度,大约 75 分钟就能破解。

至少使用 12 位,重要账号建议 16 位以上。

“看起来越复杂就越安全”

视觉复杂度和熵是两码事。Tr0ub4dor&3 看起来很复杂,但遵循了可预测的「基础词+替换」模式。mfYq8kL2nR 看起来更简单,但因为是真正随机的,熵反而更高。

更多安全策略请参阅Web 安全最佳实践

常见问题

多少比特的熵才算安全?

对于大多数在线账号,60-80 比特就能提供强保护。对于高价值目标(如主密码或加密密钥),建议 100 比特以上。每多一个比特,攻击者需要的时间就翻倍。

添加特殊字符一定能增加熵吗?

只有在字符是从完整池中随机选取时才能。可预测的替换(如 @ 代替 a、末尾加 !)几乎不增加熵,因为攻击者的字典早已包含这些模式。

4 个词的 Diceware 密码短语有多少熵?

使用标准的 7,776 词 Diceware 词表,每个词贡献 12.92 比特。四个词约 51.7 比特——对低安全需求足够。重要账号建议使用 5-6 个词(64-78 比特)。

Math.random() 能用来生成密码吗?

不能。Math.random() 是一个非密码学安全的伪随机数生成器。请在浏览器中使用 crypto.getRandomValues(),在 Node.js 中使用 crypto.randomBytes() 来生成安全随机数。

bcrypt 的 cost 参数如何影响安全性?

bcrypt 的 cost 每增加 1,哈希计算量翻倍(暴力破解也相应翻倍)。cost=12 意味着 2¹² = 4,096 次迭代,相当于在密码本身的熵之上增加了 12 比特的破解难度。

NIST 2024 年密码指南有什么变化?

NIST SP 800-63B 取消了强制复杂度要求(不再强制特殊字符、大小写混合)和定期密码轮换。新指南倾向于更长的密码(建议 15 个字符以上)、筛查已泄露密码库,以及允许所有可打印字符(包括空格)。

要点总结

  1. 熵 = L × log₂(R) —— 每多一个比特,破解所需猜测次数翻倍
  2. 长度 > 复杂度 —— 增加一个字符比扩大字符集更有效
  3. 使用密码学安全 API —— crypto.getRandomValues()crypto.randomBytes(),绝不用 Math.random()
  4. 密码管理器 + 随机生成是大多数人的最佳实践
  5. 服务端也很重要 —— 用 bcrypt 或 argon2,绝不要用 MD5 存储密码

想生成一个高熵密码?试试我们的随机密码生成器——它会实时显示每个密码的熵值分析。