zxcvbnを利用して、パスワードの推測しやすさを評価する
ブラウザでパスワード生成とパスワード保存の機能拡張が使えるようになって、パスワード管理は便利になっている一方、 いまだに推測されやすいパスワードを使い続けるユーザも一定の割合でいて、パスワード流出の事件は定期的にTech系ニュースでたびたび報じられています。
一つのサービスでパスワードが流出しても、他のサービスでは安全が保たれるように、パスワードを使い回さないように推奨されている時点で、 すでに人間がパスワードを管理することは不可能と考えられます。
Dropbox謹製のzxcvbnというライブラリを利用して、ユーザの登録時にパスワードの評価を行い、推測しやすいパスワードについては登録を受け付けないやり方を紹介します。
zxcvbnのリポジトリはこちらです。
JavaScriptだけでなく、さまざまな言語にポーティングされているのがわかります。
npm
、bower
、git clone
などでインストールしましょう。
最初は登録/パスワードリセットのhtmlのソースコードです。 id="bar"には、パスワードの強度をバーで表示します。 id="reason"には、入力されたパスワードについてのフィードバックを表示します。
<div class="card-body">
<form method="POST" action="https://tiny-services.com/register">
<input type="hidden" name="_token" value="OAFoGRw9MPsG0szU6u8xtHBXTL0PG5LVqQdp3kWz">
<div class="form-group">
<label for="name" class="form-label">Name</label>
<input id="name" type="text" class="form-input " name="name" value="" required autocomplete="name" autofocus>
</div>
<div class="form-group">
<label for="email" class="form-label">E-Mail Address</label>
<input id="email" type="email" class="form-input " name="email" value="" required autocomplete="email">
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input id="password" type="password" class="form-input " name="password" required autocomplete="new-password">
</div>
<div class="form-group">
<div class="pv2">
<label for="bar" class="form-label">Password Strength</label>
<div id="bar" style="height: 16px;">
</div>
</div>
<div class="pv2">
<div id="reason" style="height: 16px; line-height: 1.0;">
</div>
</div>
</div>
<div class="form-group">
<label for="password-confirm" class="form-label">Confirm Password</label>
<input id="password-confirm" type="password" class="form-input" name="password_confirmation" required autocomplete="new-password">
</div>
<div class="form-group">
<div class="d-flex justify-end pv2">
<button type="submit" id="submit" class="btn btn-primary">
Register
</button>
</div>
</div>
</form>
</div>
次にスクリプトの読み込み部分です。
zxcvbn.jsとpassword_validation.jsの2つを読み込んでいます。
<script src="https://tiny-services.com/js/zxcvbn.js"></script>
<script src="https://tiny-services.com/js/password_validation.js"></script>
password_validation.jsは、最初に即時関数を使って、passwordとpassword_confirmationのkeyupイベントに対して、イベントリスナとして、validate()を設定します。 その後、一度、validate()を呼び出して実行します。
(function () {
const password_input = document.getElementById('password');
password_input.addEventListener('keyup', validate);
const password_confirmation_input = document.getElementById('password-confirm');
password_confirmation_input.addEventListener('keyup', validate);
validate();
}());
validate()は、validate_password()を呼び出し、その結果をtoggle_confirmation_password()に渡します。 さらに、validate_passwords()を呼び出し、その結果をtoggle_submit_button()に渡します。
function validate()
{
const status_password = validate_password();
toggle_confirmation_password(status_password);
const status_confirmation_password = validate_passwords();
toggle_submit_button(status_confirmation_password);
}
validate_password()は、passwordの値を取得して、zxcvbnライブラリに渡して、その結果をeval_strength()関数に渡します。 scoreが4以上であればtrueを、そうでなければfalseを返します。
function validate_password()
{
const element = document.getElementById('password');
const password = element.value;
const result = zxcvbn(password);
eval_strength(result);
if(result.score >= 4) {
return true;
}
return false;
}
eval_strength()は、resultを受け取って、htmlのid=barとid=reasonを描画します。
function eval_strength(result)
{
const score = result.score;
const warning = result.feedback.warning;
const bar = document.getElementById('bar');
bar.innerHTML = ' ';
if(result.score >= 4 && result.feedback.warning.length == 0) {
reason.innerHTML = 'Your password is Strong enough.';
}
else {
const reason = document.getElementById('reason');
reason.innerHTML = ''+warning+'';
}
}
validate_passwords()は、id="password"とid="password-confirm"の値を比較して、一致すればtrueを、しなければfalseを返します。
function validate_passwords()
{
const password1 = document.getElementById('password').value;
const password2 = document.getElementById('password-confirm').value;
if(password1 == password2 && password1.length > 0) {
return true;
}
return false;
}
toggle_confirmation_password()は、引数がtrueならpassword-confirmの属性からdisabledを削除し、確認用パスワードを入力可能にします。
function toggle_confirmation_password(status)
{
const element = document.getElementById('password-confirm');
if(status) {
element.removeAttribute('disabled');
}
else {
element.setAttribute('disabled', 'disabled');
}
return status;
}
toggle_submit_button()は、引数がtrueならsubmitの属性からdisabledを削除し、送信ボタンを押下可能にします。
function toggle_submit_button(status)
{
const element = document.getElementById('submit');
if(status) {
element.removeAttribute('disabled');
}
else {
element.setAttribute('disabled', 'disabled');
}
return status;
}
- 入力したパスワードが弱く、推測しやすい場合
- 入力したパスワードが強く、推測しにくい場合
zxcvbnを使って、入力されたパスワードを評価し、強度が足りない場合には、登録を受け付けないようにすることができました。 zxcvbnは辞書を含むため、minifyした後でも819.6KBと大きいです。 そのため、ユーザ登録処理のときだけ読み込むようにしましょう。