以下是 Pankaj Parashar 的客座文章。 Pankaj 是我们关于所有 <progress>
和 <meter>
相关事宜的 驻站专家,而这篇文章正是对此的进一步佐证。 在这里,他向我们介绍了如何使用语义上最佳的选择来实现密码强度计。
许多主要的网站,如 Dropbox、Gmail 和 eBay,在注册过程中都依赖于某种指示器来向用户指示密码的强度。 该指示器可以很好地提醒用户密码破解的难度级别。

尽管这种做法并不新鲜,但我迄今为止所见的大多数密码强度指示器实现都使用 <div>
和 <span>
的旧标记来表示指示器。 随着 HTML5 的引入,我们现在可以在标记中使用 <meter>
元素,在我看来,这在语义上更准确,并且非常适合这种密码强度指示器用例。 在整篇文章中,我们将称之为**密码强度计**。
<progress>
元素?
为什么不使用 HTML5 的 顾名思义,HTML5 的 <progress>
元素用于指示任务或活动的进度。 表示密码的强度,并不是真正的任务或活动。 它不是朝着某个目标前进或曾经“完成”的事情。 因此,可以安全地得出结论,<progress>
元素不适合此用例。
如何计算密码强度?
我们将使用 Dropbox 的 zxcvbn 库来计算密码强度。 有不少其他 JavaScript 库可以计算密码强度,但 zxcvbn 非常适合我们的用例,因为
- 它提供了一个简单的 API,该 API 以密码作为输入,并返回 0 到 4 的分数作为输出,以指示密码强度(0 – 最弱,4 – 最强)。 这与我们的
<meter>
元素非常契合,该元素可以接受预定义min
-max
范围内的value
。 - 它通过检测所有可能的重叠模式,然后将其与多个英语词典、常用密码、键盘模式和重复进行匹配,来估计密码的实际强度。
- 它由 Dropbox 构建! 官方博客 提供了有关算法技术细节的更多信息。
如果您不熟悉 HTML5 的 <meter>
元素,那么 CSS-Tricks 恰好有一篇 合适的文章 可以帮助您快速上手基本知识。 我强烈建议您在继续阅读本文之前先阅读它。
基本标记
让我们从一个基本的标记开始:一个接受密码的 <input>
字段,以及一个配对的 <label>
。
<label for="password">Enter password</label>
<input type="password" id="password" required>
我们将在 <input>
下方添加 <meter>
元素以及一个文本元素,我们可以在其中确认和解释由 meter 元素表示的当前值。 由于 zxcvbn 返回 0 到 4 范围内的值,因此 meter 的最小可能值为 0
,而最大可能值为 4
。 如果未指定,则 min
属性的默认值始终为 0
。
<meter max="4" id="password-strength-meter"></meter>
<p id="password-strength-text"></p>
W3C 建议 在 meter 标签内包含当前值的文本表示,以供旧版浏览器使用。 但是,我们现在将其保留为空白,并在文章后面讨论可能的回退技术。
设置 meter 的样式
在本节中,我们将只关注 <meter>
元素的样式。 我将把其余标记的样式设置作为练习留给您。 为了了解如何设置 <meter>
元素的样式,我们需要深入浏览器内部,以分解 <meter>
元素的实现。
例如,以下是基于 Blink/Webkit 和 Gecko 的浏览器如何分解 <meter>
标签的方式

<meter>
元素在各种渲染引擎中的实现级细节。zxcvbn 库返回 0 到 4 的分数。 这意味着我们的密码强度计有五个可能的值。 我们可以使用属性选择器来定位其中的每一个,例如,meter[value="0"]
、meter[value="1"]
等。
分数代表密码强度。 分数越高,密码越难破解。 我们将用不同的颜色来表示每个分数,以便向用户提供视觉反馈。 我们可以跳过 value="0"
的样式设置,因为它将不可见。
设置 meter 条的样式
meter {
/* Reset the default appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0 auto 1em;
width: 100%;
height: 0.5em;
/* Applicable only to Firefox */
background: none;
background-color: rgba(0, 0, 0, 0.1);
}
meter::-webkit-meter-bar {
background: none;
background-color: rgba(0, 0, 0, 0.1);
}
设置 meter 值的样式
/* Webkit based browsers */
meter[value="1"]::-webkit-meter-optimum-value { background: red; }
meter[value="2"]::-webkit-meter-optimum-value { background: yellow; }
meter[value="3"]::-webkit-meter-optimum-value { background: orange; }
meter[value="4"]::-webkit-meter-optimum-value { background: green; }
/* Gecko based browsers */
meter[value="1"]::-moz-meter-bar { background: red; }
meter[value="2"]::-moz-meter-bar { background: yellow; }
meter[value="3"]::-moz-meter-bar { background: orange; }
meter[value="4"]::-moz-meter-bar { background: green; }
更新 meter
在继续之前,让我们使用 <script>
标签,将来自 cdnjs 的 zxcvbn 库包含在 body 元素之前。
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.2.0/zxcvbn.js"></script>
zxcvbn 向全局命名空间添加了一个函数。 它接受一个必需的参数(密码)并返回一个结果对象,该对象具有以下属性
- **guesses** – 估计破解密码所需的猜测次数
- **guesses_log10** – 猜测次数以 10 为底的对数
- **crack_time_seconds** – 以秒为单位的实际破解时间估计值
- **crack_time_display** – 以人类可读的格式表示的破解时间,例如“3 小时”等
- **score** – 0-4 范围内的整数(这是我们将用于 meter 的值)
- **feedback.warning** – 解释输入的密码有什么问题
- **feedback.suggestions** – 一系列建议,以帮助选择更不容易被猜到的密码
- **sequence** – zxcvbn 基于其进行猜测计算的模式列表
- **calc_time** – zxcvbn 计算答案所需的时间(以毫秒为单位)
zxcvbn 还包含一个可选的 user_inputs
参数,该参数接受一个字符串数组,它将其用作黑名单,以惩罚包含来自其他字段(如姓名、电子邮件等)的用户个人信息的密码。
现在,每次密码字段发生更改时,我们将密码传递给 zxcvbn 函数,并使用结果 score
更新 meter。 除了更新 meter 的 value
属性之外,我们还将更新伴随的文本,以向用户指示密码的强度。
首先,我们将分数映射到人类可读的格式,
var strength = {
0: "Worst",
1: "Bad",
2: "Weak",
3: "Good",
4: "Strong"
}
其次,将监听器附加到密码字段,以侦听更改,然后更新 meter 和文本指示器。
var password = document.getElementById('password');
var meter = document.getElementById('password-strength-meter');
var text = document.getElementById('password-strength-text');
password.addEventListener('input', function() {
var val = password.value;
var result = zxcvbn(val);
// Update the password strength meter
meter.value = result.score;
// Update the text indicator
if (val !== "") {
text.innerHTML = "Strength: " + strength[result.score];
} else {
text.innerHTML = "";
}
});
这个 演示示例 还使用了 feedback.warnings
和 feedback.suggestions
来为用户提供相关且有用的反馈,帮助他们选择一个更不容易被猜到的密码。
查看 Pankaj Parashar 在 CodePen 上创建的示例 密码强度计(@pankajparashar)。
如果由于任何原因,演示在您的浏览器中无法正常运行,您可以 观看此视频。
备用方案
目前,我们的密码强度计在所有 支持 HTML5 <meter>
元素的浏览器中都能很好地工作。好消息是,我们不必担心那些不支持它的浏览器。这些浏览器将简单地忽略 <meter>
标签,并呈现计量器后面的文本,这是一个不错的备用方案,可以向用户指示密码的强度。
如果您决心在所有浏览器中提供一致的用户体验,则可以使用 <div>
和 <span>
标记的组合在 <meter>
元素内模拟计量器的外观。不理解 <meter>
标签的浏览器将简单地忽略它,而是呈现其中的标记。我在 CSS-Tricks 上关于同一主题的 上一篇文章 的备用方案部分详细描述了此方法。
从用户体验的角度来看,密码强度计好吗?
本文无意引发关于密码强度计是否好的争论。可能存在 合理 和 有道理 的论点,支持双方观点。但是,大多数争论源于算法无法提供密码强度的真实度量。我认为 Dropbox 的 zxcvbn 库做到了这一点,因为它提供了更真实的估计,即破解密码有多难。
是否在您的设计中使用它,取决于您。但是,如果您决定尝试,请确保使用 HTML5 <meter>
元素!
如果您要为计量器指示器进行色调转换,我建议您调换橙色和黄色(红色 > 橙色 > 黄色 > 绿色)。
是的。这是颜色光谱的自然顺序的一部分:红色 > 橙色 > 黄色 > 绿色 > 蓝色 > 靛蓝 > 紫罗兰色。我一直使用以下助记符来记住顺序:Robert of York gained battles in vain。虽然,我在 Google 上搜索后才发现,实际上是Richard of York 赢得了这些战役。问题是,我甚至不知道 Robert 或 Richard 是谁,以及发生了哪些战役!
有道理。感谢 @Kevin 和 @Wayne 的意见。我以后会记住这一点。
@Wayne 助记符实际上是 Richard Of York Gave Battle In Vain。它指的是英格兰的理查三世(约克家族成员)以及他在 1485 年博斯沃思战役中被兰开斯特军队(由亨利·都铎领导,后来成为英格兰的亨利七世)击败。
我理解正确吗?密码被发送到另一个网站(api)?明文发送?这不好吗?(真诚发问)。
不,zxcvbn api 加载在客户端。在用户提交表单之前,不会发送任何内容,此时信息(希望)将发送到您的服务器。
这是不正确的。库的源代码包含一个庞大的字符串列表,这些字符串是密码中常见的。它根据这些字符串和通用模式进行匹配,以获得结果。没有网络活动。没有传输明文密码。
我认为 api 作为 JS 文件从
https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.2.0/zxcvbn.js
导入到实现中。因此,不会将任何密码发送到任何其他网站。
您可以通过在填充时为条形添加过渡来使事情变得更加“美观”。
此外,如果您这样做,我会为 0 值设置样式,因为当您删除密码时,计量器在转换回 0 时会闪烁为绿色(默认值)。
http://codepen.io/fatmedia/pen/PZYrBr – 这是一个演示示例 :-)
zxcvbn.js 和 javascript 应该放在前面,因为它们需要访问已完成的 DOM。
哈哈,我实际上输入了“aaa”…