密码强度 `meter`

Avatar of Pankaj Parashar
Pankaj Parashar

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

以下是 Pankaj Parashar 的客座文章。 Pankaj 是我们关于所有 <progress><meter> 相关事宜的 驻站专家,而这篇文章正是对此的进一步佐证。 在这里,他向我们介绍了如何使用语义上最佳的选择来实现密码强度计。

许多主要的网站,如 DropboxGmaileBay,在注册过程中都依赖于某种指示器来向用户指示密码的强度。 该指示器可以很好地提醒用户密码破解的难度级别。

展示了 eBay(顶部)、Gmail(中间)和 Dropbox(底部)的密码强度指示器,它们以不同的形式呈现,但本质上代表了相同的信息。

尽管这种做法并不新鲜,但我迄今为止所见的大多数密码强度指示器实现都使用 <div><span> 的旧标记来表示指示器。 随着 HTML5 的引入,我们现在可以在标记中使用 <meter> 元素,在我看来,这在语义上更准确,并且非常适合这种密码强度指示器用例。 在整篇文章中,我们将称之为**密码强度计**。

为什么不使用 HTML5 的 <progress> 元素?

顾名思义,HTML5 的 <progress> 元素用于指示任务或活动的进度。 表示密码的强度,并不是真正的任务或活动。 它不是朝着某个目标前进或曾经“完成”的事情。 因此,可以安全地得出结论,<progress> 元素不适合此用例。

如何计算密码强度?

我们将使用 Dropbox 的 zxcvbn 库来计算密码强度。 有不少其他 JavaScript 库可以计算密码强度,但 zxcvbn 非常适合我们的用例,因为

  1. 它提供了一个简单的 API,该 API 以密码作为输入,并返回 0 到 4 的分数作为输出,以指示密码强度(0 – 最弱,4 – 最强)。 这与我们的 <meter> 元素非常契合,该元素可以接受预定义 min-max 范围内的 value
  2. 它通过检测所有可能的重叠模式,然后将其与多个英语词典、常用密码、键盘模式和重复进行匹配,来估计密码的实际强度。
  3. 它由 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> 标签的方式

说明了 HTML5 <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> 标签,将来自 cdnjszxcvbn 库包含在 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.warningsfeedback.suggestions 来为用户提供相关且有用的反馈,帮助他们选择一个更不容易被猜到的密码。

查看 Pankaj Parashar 在 CodePen 上创建的示例 密码强度计@pankajparashar)。

如果由于任何原因,演示在您的浏览器中无法正常运行,您可以 观看此视频

备用方案

目前,我们的密码强度计在所有 支持 HTML5 <meter> 元素的浏览器中都能很好地工作。好消息是,我们不必担心那些不支持它的浏览器。这些浏览器将简单地忽略 <meter> 标签,并呈现计量器后面的文本,这是一个不错的备用方案,可以向用户指示密码的强度。

如果您决心在所有浏览器中提供一致的用户体验,则可以使用 <div><span> 标记的组合在 <meter> 元素内模拟计量器的外观。不理解 <meter> 标签的浏览器将简单地忽略它,而是呈现其中的标记。我在 CSS-Tricks 上关于同一主题的 上一篇文章 的备用方案部分详细描述了此方法。

从用户体验的角度来看,密码强度计好吗?

本文无意引发关于密码强度计是否好的争论。可能存在 合理有道理 的论点,支持双方观点。但是,大多数争论源于算法无法提供密码强度的真实度量。我认为 Dropbox 的 zxcvbn 库做到了这一点,因为它提供了更真实的估计,即破解密码有多难。

是否在您的设计中使用它,取决于您。但是,如果您决定尝试,请确保使用 HTML5 <meter> 元素!