使用熵计算用户友好的强密码
带有具体且老旧密码规则的注册表单对于使用密码管理器的人来说可能会相当令人沮丧。例如,“必须至少包含一个特殊字符,但只能使用 !@#$ 中的这些字符。”这样的规则尝试帮助用户实现更高的安全性,但最终却往往造成更多的烦恼,而非更高的安全性。
在构建 PlanetScale 的注册表单时,我们希望能够强制使用强密码,同时兼容密码管理器。在研究这个问题后,我们发现最佳的方法是采用基于熵(entropy)的密码强度计算。
基于熵的密码强度
密码强度可以定义为计算机成功破解密码所需的尝试次数:
2^(熵) = 破解密码所需的尝试次数
如果你的计算机每秒可以进行 1000 次尝试,你可以估算出密码能抵挡攻击的时间。
弱密码示例
密码:mike1
这个简单密码的熵约为 16。以每秒 1000 次尝试的速度,只需 60 秒就能够破解:
2^16 / 1000 = ~60 秒
强密码示例
密码:cTzk9*R6-uf9
这个密码通常可以满足大部分网站的密码规则要求,它含有数字、特殊字符以及大小写字母。
2^61 / 1000 = 3650 万年
长而强的密码示例
密码:mikemikemikemikem
这个密码的熵与上述密码相似,但不会通过大多数常见的密码规则。
2^61 / 1000 = 3650 万年
熵比具体规则更好
最后两个例子突出了特定密码规则所带来的挫败感。无论是 cTzk9*R6-uf9
还是 mikemikemikemikem
都有大约 61 位熵,使它们很难被猜测。
示例中的 mikemikemikemikem
很难满足大多数密码强度规则。
我们仍然认为第一个密码更强,因为它不包含字典单词(更重要的是没有我的名字!)。但这并不意味着第二个密码不够安全以用于大多数应用场景。
减缓尝试速度
从这些例子中,我们可以得出一个清晰的教训:攻击者能进行的尝试越多,破解密码的速度就越快。以下是一些可以提高应用防御安全性的方法:
- 对登录表单添加速率限制:这是一种简单的方法,可以保护您的登录表单免于被程序自动尝试。
- 为密码存储使用密码哈希算法:这些算法设计得故意较慢,以减少尝试速度。我们选择使用 Argon2。
字典单词与先前泄漏密码
除了熵检查外,在设计强密码验证时还有其他需要考虑的方法。
字典单词
密码破解工具通常使用预先准备好的字典单词列表,以提高破解密码的速度。strong_passwords
gem 包含了一个字典单词列表,并在实际使用字典单词时调整密码强度评分。
泄漏的密码
如果密码曾经被泄漏,其再强也毫无用处。将 haveibeenpwned
API 集成到您的密码验证系统中,是保护用户的另一种有效方式。
更好的注册表单
了解密码强度和熵的概念后,我们能够利用这些知识来改进应用程序的注册体验。
与其显示规则列表并进行验证,您可以实现一个密码强度仪表,通过测量密码的熵为用户即时提供反馈,效果更好。它不仅与密码管理器兼容,还能在用户手动输入密码时快速提供反馈。
以下是我们在 PlanetScale 注册表单中实施的方式:
我们如何实现它
我们的授权页面是一款基于 Ruby on Rails 的应用程序。我们使用了 strong_password
gem 和 auto-check-element
Web 组件,为用户在输入密码时即时提供密码强度反馈。
密码表单界面
我们在现有密码表单上包裹了 auto-check-element
。这个 Web 组件在用户输入时将密码的值发送到后端。后端计算出密码的熵,并返回渲染的仪表 SVG,向用户展示密码强度的进展。
HTML1<auto-check csrf="<%= form_authenticity_token %>" src="/users/password-strength" required="">
2 <div class="mb-1.5 flex items-center justify-between leading-none">
3 <%= f.label(:password, "New password", class: "mb-0") %>
4 <div class="js-password-strength-container" aria-live="polite"></div>
5 </div>
6 <%= f.password_field(:password, class: "js-password-strength", autofocus: true, autocomplete: "new-password", required: true) %>
7</auto-check>
这个表单元素还添加了一点额外的 JavaScript,用于在每次检查后更新仪表显示。
密码强度的计算器
控制器代码决定密码强度的百分比,然后渲染仪表表单。
Ruby1def create
2 checker = User.password_checker
3 entropy = checker.calculate_entropy(params[:value] || "")
4 percentage = (entropy / STRONG_ENTROPY) * 100
5
6 percentage = 100 if percentage > 100
7
8 render(partial: "users/shared/password_strength_meter", locals: { strength: percentage.to_i })
9end
通过基于熵的计算和即时强度反馈,我们不仅提高了用户体验,还提升了应用的安全性。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:http://www.choupangxia.com/2025/05/20/using-entropy-for-user-friendly-strong-passwords/