安全指南
基于 Web Crypto 与拒绝采样的客户端密码生成白皮书
说明 PwdGen 在浏览器本地生成密码时使用的随机数来源、拒绝采样、熵值边界和隐私边界。
摘要
PwdGen 的客户端参考生成方法由三部分组成:浏览器或运行时的 Web Crypto 提供随机值,拒绝采样把随机值映射为无偏的有限范围索引,再用 Fisher-Yates 洗牌把必选字符类别分布到最终密码中。这个方法保持足够小,便于审查,并可在网页、API 适配层、CLI 和准备中的 pwdgen-core 包之间复现。
这是一份工程说明,不声称创造了新的密码学原语,也不声称已经成为行业标准或通过第三方安全审计。
随机数来源
生成器通过 crypto.getRandomValues() 请求密码学强随机值。Web Crypto 实现通常使用由宿主环境高质量熵源播种的密码学安全伪随机数生成器。实际浏览器会把底层随机性委托给操作系统和平台加密能力。
PwdGen 不声称每一次生成都会直接读取 CPU 硬件噪声源。更准确的说法是:浏览器通过 Web Crypto API 提供 CSPRNG 输出,而 PwdGen 避免使用非密码学用途的伪随机接口。
为什么不用普通随机数
Math.random() 这类普通脚本随机数并不面向安全场景设计。它适合模拟、动画和普通 UI 随机行为,但不适合作为密码、重置码、签名密钥或其他凭据的随机来源。
如果当前环境没有 Web Crypto,PwdGen 应显示兼容性错误,而不是悄悄回退到弱随机数并生成看似可用的密码。
拒绝采样
随机整数通常来自 2 的幂范围,而密码字符集大小很少刚好整除这个范围。如果直接取模,某些字符会比其他字符略微更容易出现。
PwdGen 使用拒绝采样:
- 读取一个 32 位无符号整数。
- 计算 32 位范围内可被字符集大小整除的最大边界。
- 丢弃超过边界的值。
- 只对完整范围内的值取模。
这样可以让每个字符被选中的概率保持一致,避免取模偏差。
字符覆盖与理论熵
对于有密码规则限制的场景,生成器会先从每个启用字符类别中选取一个字符,再从合并字符集中填充剩余位置,最后进行洗牌。这样可以满足常见密码策略,同时避免某一类字符总是出现在固定位置。
理论熵估算公式为:
bits = length × log2(uniqueAlphabetSize)
这个数值是“从指定字符集均匀随机选择”时的理论上限。它不代表被泄露、复用、手工修改、设备被攻陷或目标服务存储方式不安全时仍然安全。
隐私边界
在浏览器本地工具中,生成值只存在于页面内存和可见结果区域,直到用户复制或导出。PwdGen 的本地生成器不会把生成密码发送到服务器,不会写入 URL,也不会写入分析事件。
这个边界无法防御恶意浏览器扩展、剪贴板管理器、操作系统被攻陷、屏幕录制、钓鱼页面,或用户把密码粘贴到不可信服务中。
可复现实现
公开的 pwdgen-cli 包和准备中的 pwdgen-core 包用于让生成模型离开网页后仍可检查。测试覆盖字符类别、排除字符、无效配置、熵公式,以及不使用弱随机回退路径。
目标不是把 PwdGen 宣称为私有标准,而是让实现足够小、足够透明,方便开发者阅读、测试,并在自己的信任边界需要变化时替换。