Webサービスなどでアカウント登録を行う際、自分が使おうとしているパスワードがどれぐらい安全かを判定して教えてくれる場合があります。
「zxcvbn」というDropbox社が提供しているライブラリを用いることで、強度判定を比較的簡単に実装できるため、備忘録として残したいと思います!
zxcvbnについて
先述した通り、zxcvbnはDropbox社によって開発されたパスワードの強度評価ライブラリです。
ライブラリを使用することで、ユーザーが入力したパスワードの強度を0から4の段階で評価して、そのスコアに基づいて、ユーザーにパスワードの改善を促すフィードバックを表示することができます。
強度の判定はクライアントサイドでの実施となり、ユーザーのパスワードがサーバーに送信されることがないので、セキュリティリスクは小さいようです。
下記が公式のリポジトリになります。
昨今、Webアプリを開発するとなると、フロント部分はReactやVue.jsなどを利用し、ライブラリ導入にあたってはnpmやyarnを使ってインストール→コンポーネントとしてimport、がよくあるケースかと思います。
今回はHTMLとJavaScript、CSSのシンプルな構成での備忘録となってしまいますが、いずれReactでの利用方法も残したいと思います!
導入手順
下記のとおり、強度を表示するだけであれば少ない手順で実装することができます。
- zxcvbnのライブラリを読み込む。今回はjsdelivr.netのCDNを使用
- HTML要素を準備する。パスワードの入力フィールドと、フィードバックを表示するテキストエリアを用意(今回の実装では強度に合わせて表示が変わるバーを用意しました)
- zxcvbnライブラリを使用してパスワードの強度を評価し、結果を表示するJavaScriptコードを書く
- 必要があればCSSを使って装飾する
実装例
今回の実装例では、下記のような流れで動作する形となっています。
- <input class=”form__password-input” id=”password” type=”password” name=”password” required> にテキストが入力されるたびに、zxcvbnライブラリを使用してパスワードの強度を判定
- パスワードの強度に基づいて<div id=”passwordStrengthBar”> の幅とスタイルを動的に変更する。強度が低い場合はバーを短く赤色にし、高い場合は長く緑色に変更する
- <div class=”form__password-strength-text” id=”passwordStrengthText”> に、zxcvbnから得られたパスワードの強度スコアに応じたメッセージを表示
その他に、パスワード入力欄によく実装される機能を追加しています。
- <span class=”form__toggle-password” id=”togglePassword”> (目のアイコン)をクリックすると、パスワードフィールドのtype属性がpasswordからtextへ切り替わり、入力されたパスワードが表示される
- パスワード入力フィールドにフォーカスがある状態でエンターキーが押されてもフォームが誤送信されないよう、KeydownイベントをpreventDefaultして、動作をキャンセルする
動作サンプル
See the Pen zxcvbn sumple by Yoshihiro Hotta (@yoshihiro-hotta) on CodePen.
HTML
<form class="form" id="passwordForm">
<!-- タイトル -->
<h2 class="form__title">パスワードの強度を確認しよう</h2>
<!-- パスワード入力フィールドと表示切替アイコンのコンテナ -->
<div class="form__password-container">
<!-- パスワード入力フィールド -->
<input class="form__password-input" id="password" type="password" name="password" required>
<!-- パスワード表示切替アイコン -->
<span class="form__toggle-password" id="togglePassword">
<i class="form__input-icon fas fa-eye"></i>
</span>
</div>
<!-- パスワード強度表示バーのコンテナ -->
<div class="form__strength-bar-wrap" id="passwordStrengthContainer">
<div id="passwordStrengthBar"></div>
</div>
<!-- パスワード強度のサブタイトル -->
<h3 class="form__sub-title">パスワードの強度</h3>
<!-- パスワード強度のテキスト表示エリア -->
<div class="form__password-strength-text" id="passwordStrengthText"></div>
</form>
CSS
body {
margin: 0;
}
.form {
width: 100%;
max-width:450px;
margin: 0 auto;
padding: 20px;
}
.form__title {
display: block;
margin: 0;
font-size: 20px;
}
.form__sub-title {
display: block;
margin: 10px 0 0;
font-size: 16px;
}
.form__strength-text {
margin-top: 0;
font-size: 16px;
}
.form__password-container {
position: relative;
display: flex;
align-items: center;
width: 100%;
margin: 5px auto 0;
}
.form__password-input {
padding: 5px 30px 5px 7px;
flex-grow: 1;
font-size: 18px;
border: 1px solid #ccc;
}
.form__password-input:focus {
outline: none;
border: 1px solid #ccc;
}
.form__toggle-password {
position: absolute;
right: 10px;
color: #aaa;
cursor: pointer;
}
.form__password-strength-bar {
width: 100%;
}
.form__input-icon {
font-size: 16px;
}
#passwordStrengthBar {
height: 10px;
width: 0%;
transition: width 0.5s ease;
}
.strength0 {
width: 5%;
background-color: #ff3e36;
}
.strength1 {
width: 30%;
background-color: #ff691f;
}
.strength2 {
width: 50%;
background-color: #ffda00;
}
.strength3 {
width: 70%;
background-color: #0be881;
}
.strength4 {
width: 100%;
background-color: #05c46b;
}
JavaScript
// 必要なDOM要素を取得
const passwordInput = document.getElementById('password');
const strengthBar = document.getElementById('passwordStrengthBar');
const strengthText = document.getElementById('passwordStrengthText');
const togglePassword = document.getElementById('togglePassword');
const icon = togglePassword.querySelector('i');
// パスワードの強度を更新する関数
const updatePasswordStrength = (password) => {
// パスワードが入力されていない場合は、ゲージを非表示にし、テキストを更新
if (password.length === 0) {
strengthBar.style.width = '0%';
strengthText.innerHTML = 'パスワードを入力してください';
return;
}
// パスワードの強度を評価
const result = zxcvbn(password);
// 強度に応じてゲージのスタイルとクラスを更新
strengthBar.className = '';
strengthBar.classList.add(`strength${result.score}`);
strengthBar.style.width = result.score > 0 ? `${result.score * 25}%` : '10%';
// 強度レベルに応じたメッセージを配列から選択して表示
const strengthMessages = [
'非常に弱い - 文字数を増やしてください',
'弱い - もう少し複雑にしてください',
'普通 - より強いパスワードを検討してください',
'強い - 良いパスワードです',
'非常に強い - 非常に安全なパスワードです'
];
strengthText.innerHTML = strengthMessages[result.score];
};
// パスワードの表示・非表示を切り替える関数
const togglePasswordVisibility = () => {
// パスワード入力欄のタイプを切り替え
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
// アイコンのクラスを切り替えて、表示・非表示の状態を示す
icon.classList.toggle('fa-eye');
icon.classList.toggle('fa-eye-slash');
};
// エンターキーでのフォーム送信を防ぐ関数
const preventFormSubmissionOnEnter = (event) => {
if (event.key === 'Enter') {
event.preventDefault(); // エンターキーのデフォルト動作を防止
}
};
// イベントリスナーを追加
passwordInput.addEventListener('input', (event) => updatePasswordStrength(event.target.value));
togglePassword.addEventListener('click', togglePasswordVisibility);
passwordInput.addEventListener('keydown', preventFormSubmissionOnEnter);
最後に
アカウント作成画面で、入力されたパスワードの強度が0〜3の場合は、パスワードとして認めず次の画面に進めないようにする、なども工夫次第で実装できそうですね。
また、今回使用したzxcvbnの機能はごく一部で、下記のような機能も持っているようです。
- クラックにかかるであろう時間を表示する
- zxcvbnが用意している警告文を出す(『これはトップ 10 によくあるパスワードです』など)
- パスワードの改善案を示す(『もう少し文字数を増やすべき』など)
記事を書き終える頃に気づいたのですが、Typescript対応版の「zxcvbn-ts」などもあるようなので、こちらでの実装も後日試してみようと思います!