技术安全 · 7 分钟阅读

正确的生成器,
正确的语言

并非所有密码生成器都一样。你的保险库由哪种语言编写,也绝不是工程师才需要关心的细节。本文解析熵、生成模式,以及为什么 Rust 正在成为底层安全的关键选择。

发布于 2026 年 6 月 · 阅读时间 7 分钟

当密码管理器声称“生成强密码”时,有两个问题必须回答:它真的不可预测吗(随机性的质量),以及显示之后它在内存中会发生什么?前者取决于生成模式,后者取决于运行应用程序的语言。本文会同时解释这两点。

~100 bits
16 字符随机密码(非常强)
~77 bits
6 个单词的 Diceware 口令(EFF)
12,9 bits
每个 Diceware 单词增加的熵(7,776 词表)

01 / 基础熵:唯一真正重要的指标

密码强度不应按主观感受中的“复杂度”衡量,而应按衡量:也就是攻击者面对的不确定性位数。可能组合越多,暴力破解成本就越呈爆炸式增长。一个从完整 ASCII 字符集中随机抽取的 16 字符密码约有 100 bits 熵,属于“非常强”的范畴。

一个经常被忽视的关键点是:只有当随机源是 CSPRNG(加密安全伪随机数生成器)时,熵才有意义。普通的 Math.random() 是可预测的,会让其余设计全部失效。优秀实现会从操作系统读取熵:Unix 下的 /dev/urandom 或 getrandom(2),Windows 下的 CNG。

02 / 模式四类生成器

优秀的管理器不应只提供“一个”生成器,而应提供多种适配不同用途的模式。规则很简单:可记忆性要用长度来换。下面是四类方式,从最高密度到最贴近人类记忆依次排列。

纯随机

j7$kQ!mP2#xR9vL@
~100 bits,16 字符
每个字符的信息密度最高。适合存放在保险库中的账户密码,也就是从不需要凭记忆输入的密码。默认应优先使用这种模式。

口令短语(Diceware / EFF)

Granite-Bicycle-Phantom-Violet-9
~77 bits,6 个单词
从 7,776 个单词的词表中随机抽取。屏幕上看起来更长,但几秒钟内就能记住。这是主密码的理想选择,因为它绝不应该被写在别处。

可记忆 / 可发音

Crimson7!Falcon$Reef
中等熵
采用“单词-数字-符号”的模式,更容易大声读出,也更适合在手机上输入。这是一种可接受的折中,但在长度相同的情况下熵更低:如果目标是可记忆性,口令短语仍然更好。

数字 PIN

8392 4471
较低,仅限特定用途
仅适用于只接受数字的场景。除受限环境外,应避免使用。

正确习惯:让熵匹配用途

对于受登录页面限速保护的 Web 账户,攻击者通常被限制在每秒几百次尝试以内:即使 50 bits 也足以支撑极其漫长的破解时间。超高熵只对离线机密有意义,例如加密密钥或加密钱包 seed,因为这类攻击可以被大规模并行化。 对于这些极高价值的机密,使用真实物理骰子(严格意义上的 Diceware)仍然是终极选择:它消除了对 CSPRNG 质量或受攻陷浏览器的任何依赖。

03 / 底层机制为什么 Rust 改变了局面

生成一个好密码只是工作的一半。另一半是:不要让它在内存中游荡。密码一旦显示或解密,就会存在于 RAM 中,而这正是应用程序所用语言变得决定性的地方。

Microsoft 报告称,其遇到的安全漏洞中有 70% 是内存 bug(buffer overflow、use-after-free 等)。Google 在 Chrome 和 Android 上也观察到相同比例。Rust 通过所有权模型(ownership)和借用模型(borrowing),在编译期消除了这些漏洞类别,而且不需要垃圾回收器。

Google 的数据很有说服力:Android 的内存漏洞从 2019 年的 223 个降至 2024 年的不到 50 个,首次跌破总漏洞数的 20%。新的 Rust 代码相较历史 C/C++ 代码,内存漏洞密度最高可降低 1000 倍,同时 rollback 减少 4 倍,代码审查时间减少 25%。

1. 没有垃圾回收器的内存安全。 Java 或 Python 等语言同样具备内存安全性,但它们的 garbage collector 会以非确定性的方式释放内存:秘密可能无限期残留其中,暴露在 memory dump 或 cold-boot 攻击之下。Rust 会在数据不再使用时确定性地释放内存。

2. 使用 zeroize 显式擦除。 Rust 并不保证已释放内存会被覆盖。zeroize crate 补上了这个缺口:把秘密包装在 Zeroizing 中,就能保证它在离开作用域的确切时刻被零覆盖,这通过编译器无法优化掉的 volatile 写入实现。

3. 强制正确使用密码学的类型系统。 Rust 严格的类型系统可以限定敏感数据如何被使用,以及能被使用多少次。例如,可以建模一把只能使用一次的密钥,编译器会拒绝任何重复使用。

自动内存擦除use zeroize::Zeroizing;

fn unlock_vault(master: &str) {
    let dek = Zeroizing::new(derive_key(master));
    // ... 解密保险库 ...
}   // `dek` 会在此处自动被零覆盖
在 Google,采用 Rust 后,Android 内存漏洞占比从 76%(2019)降至 24%(2024),远低于行业常见水平(~70%)。

技术上要诚实:Rust 不是魔法棒

unsafe 代码和第三方依赖仍然是攻击面。Google 就曾在投产前修复过 Rust 解析器中一个 unsafe 代码块里的漏洞(CVE-2025-48530)。内存安全是一防护,而不是终点:它必须与纵深防御结合,例如 Argon2id 等强健 KDF、zero-knowledge 架构、自动锁定和 MFA。

垃圾回收语言

  • 内存释放时间不确定
  • 秘密会在内存中残留未知时长
  • 显式擦除困难或不可行
  • 暴露于 dump 与 cold-boot 攻击

Rust

  • 确定性释放(ownership)
  • 通过 zeroize 立即擦除
  • 没有 buffer overflow 或 use-after-free
  • 密码学使用错误在编译期被捕获

04 / 要点你应向密码管理器提出的要求

在选择或评估密码管理器时,可以直接问四个具体问题:

检查清单

  • 1. 生成器是否基于系统 CSPRNG,并以 bits 显示熵?
  • 2. 是否为主密码提供 Diceware/EFF 口令短语模式
  • 3. 生成过程是否本地执行(client-side),不会经过服务器?
  • 4. 敏感核心是否用内存安全语言编写,并显式擦除秘密?

本文为教学性质的信息内容。引用的统计数据(Google/Android、Microsoft、Diceware/EFF)来自上方列出的来源。Rust 代码示例仅用于说明,并经过简化。

安全的生成方式。为长久使用而设计的保险库。

高熵密码、可记忆口令短语,以及为内存安全打造的核心。

了解我们的方法