zxcvbn とは?
オンラインストレージサービスを提供している Dropbox が開発、実際に使用されているパスワード強度推定ツールです。
以下の場所からダウンロードできます。
dropbox/zxcvbn - GitHub
https://github.com/dropbox/zxcvbn
JavaScriptファイルだけであるのなら、こちらからmapファイルも含めてダウンロードできます。
https://github.com/dropbox/zxcvbn/tree/master/dist
本稿で使うJavaScript以外にも、いろいろな言語にフォークされておりますので、必要あらば上記ページのリンク先をご参考にしてください。
実際、zxcvbn が、どのような仕様で、つまりどういった設計思想の元に開発されているかは、以下のブログ記事から読むことができます。2012年時点での記事ですが、他のウェブサービスとも比較して、「強度」「推定」の計算と指針が大きくちがうことが示されています。
https://blogs.dropbox.com/tech/2012/04/zxcvbn-realistic-password-strength-estimation/
英語を読むのがシンドイという方は、一応拙訳ですが、Qiitaに日本語訳の記事を投稿しています。
Dropbox開発のzxcvbn(パスワード強度メーター)のブログ記事を全訳してみた
https://qiita.com/hibara/items/37ba7a1f8ba2d54ae600
実際に動いているサンプルページ
zxcvbn.js
を実装した、index.html
をウェブページとしてアップロードしています。こちらから動作を確認できます。
https://hibara.github.io/zxcvbn-js-test/
なお、これらのサンプルページのソースコードは、GitHub で公開しています。こちらを参考にされながら、本稿を読み進めていくと理解が深まるでしょう。
hibara/zxcvbn-js-test - GitHub
https://github.com/hibara/zxcvbn-js-test
使い方
お使いのウェブページ内のソース部分に以下を追加しまます。
<script src="js/zxcvbn.js"></script>
書き方は、
zxcvbn(password, user_inputs=[])
となります。第一引数に「パスワード文字列」、第二引数には「追加辞書」を指定します。実際のコードにしてみたのが、以下です。
const detect_password = () => {
let user_id = document.getElementById("user-id").value;
// zxcvbn判定
const result = zxcvbn(document.getElementById("password").value, [user_id,]);
// ...
}
関数 zxcvbn
の第二引数は、チェックする追加辞書の配列です。この例では、ユーザーIDの名前をパスワードに使っていないかをチェックするために辞書へ追加しています。
その他にも、基本的には zxcvbn は、アメリカ製ですので、アメリカのテレビ番組や有名人などが辞書に入っていて、必ずしも日本文化に即したパスワードチェックを行っていません。もし独自の日本語に即した推測されやすいパスワード辞書があれば、ここから追加することができます。
zxcvbn.js が返す結果データについて
前の章で、関数 zxcvbn
が返してきた変数、result
に何が入ってくるかについて、ここでは詳しく説明していきます。
以下、GitHub の README.md に書いてあるので、それを以下に拙訳(意訳)します。
変な訳し方があったら、コメントからご指摘いただけると助かります。
result.guesses # パスワードの解読に必要な推定推測値
result.guesses_log10 # result.guesses の桁数
result.crack_times_seconds # いくつかのシナリオで基づいた推定クラック時間(秒数で)の
# 大雑把な見積もり一覧
{
# パスワード認証の試行をレート制限するサービスに対するオンライン攻撃。
online_throttling_100_per_hour
# レート制限しないサービスへのオンライン攻撃、
# または、攻撃者がレート制限をすり抜けた場合
online_no_throttling_10_per_second
# オフライン攻撃。複数の攻撃者を想定し、
# bcrypt, scrypt, PBKDF2 のような適度な作業効率での、
# 適切にユーザー毎の salt 設定と、slow hash 関数を使っている。
offline_slow_hashing_1e4_per_second
# ユーザー固有に salt 設定を行ったオフライン攻撃だが、
# SHA-1、SHA-256、または MD5 の fast hash 関数を使っている場合。
# コア数やマシン数に応じて、1秒間に10億~1兆回の広範囲で合理的な推測が可能。
# おおよそ、秒間10B。
offline_fast_hashing_1e10_per_second
}
result.crack_times_display # result.crack_times_seconds と同じ内容、
# より分かりやすい文字列表記をしています:
# "1秒未満"、"3時間"、"何世紀 "など。
result.score # 0〜4 の、Integer(強度バーの導入に役立つ)
0 # かなり推測しやすい: 危険なパスワード。(推測値 < 10の3乗)
1 # とても推測しやすい: throttleを用いたオンライン攻撃からは守れる。(推測値 < 10の6乗)
2 # やや推測しやすい: throttleを用いないオンライン攻撃からは守れる。(推測値 < 10の8乗)
3 # 安全で推測しにくい: オフラインの slow hash シナリオから普通に守れる。(推測値 < 10の10乗)
4 # とても推測しにくい: オフラインの slow hash シナリオから強力に守れる。(推測値 >= 10の10乗)
result.feedback # パスワードを選択する助けになるよう言葉によるフィードバック。 score <= 2 のとき。
result.feedback.warning # 何が間違っているのかを説明します。たとえば「これはよく使われるパスワードのトップ10です」。
# 常に設定されているわけではありません -- 時には空の文字列が入ります。
result.feedback.suggestions # より推測できないパスワードを選択するの助けとするため、
# 提案リスト(文字列が入っていないこともある)。たとえば、「他の単語を追加する」
result.sequence # zxcvbn が推定計算のベースとしている
# パターンのリスト。
result.calc_time # 答えを計算するのにどれくらいの時間がかかったかを
# ミリ秒単位で表示します。
result.score
もっとも重要で、使い途が多いのは、この値でしょう。基本的に zxcvbn
では、result.score <= 2
は、すべて「推測しやすい」に分類され、非推奨(場合によっては危険)となります。
もし強度メーターのようなものを設置する場合は、2以下の値は「危険」を示す色にする必要があるでしょう。
result.crack_times_display
補足すると、result.crack_times_display
は、前段の result.crack_times_seconds
の値を分かりやすく「時間」等に置き換えた内容が Object として返ってきます。要するにそのパスワードをクラックするおおよその時間を表示します。
- result.crack_times_display["online_throttling_100_per_hour"]
- result.crack_times_display["online_no_throttling_10_per_second"]
- result.crack_times_display["offline_slow_hashing_1e4_per_second"]
- result.crack_times_display["offline_fast_hashing_1e10_per_second"];
で、それぞれ取得できます。
ソースコード内を見る限りでは、以下のいずれかの組み合わせの表現で返ります。
// x = 数値
'less than a second',
x + 'seconds',
x + 'minutes',
x + 'hours',
x + 'day',
x + 'month',
x + 'year',
'centuries', // 何世紀もかかる
result.feedback.suggestions
ちなみに、result.score > 2
だと、これらのフィードバックは返りません。
いわゆる、より推測されにくいパスワードにするにはどうしたら良いか?という提案が返ります。ソースコードを見る限り、以下に列挙した文章の提案が返るようです。
大別してこのフィードバックには、warning
と、suggestions
で構成されます。ついでに拙訳も付けておきます。
suggestions: [
// いくつかの単語を使用し、一般的なフレーズを避けてください
"Use a few words, avoid common phrases",
// 記号、数字、大文字は必要ありません。
"No need for symbols, digits, or uppercase letters"
];
suggestions: [
// 別の言葉を追加します。珍しい言葉の方が良いでしょう。
'Add another word or two. Uncommon words are better.'
];
{
warning: [
// 直線的なキー配列は推測しやすい
'Straight rows of keys are easy to guess',
// 短いキーボードパターンは推測しやすい
'Short keyboard patterns are easy to guess'
],
suggestions: [
// ターン数を増やしてより長いキーボード配列パターンを使ってください
'Use a longer keyboard pattern with more turns'
]
}
{
warning: [
// 「aaa」といった繰り返しは推測されやすいです。
'Repeats like "aaa" are easy to guess',
// 「abcabcabcabc」のような繰り返しは、「abc」よりもわずかに推測しにくいだけです。
'Repeats like "abcabcabc" are only slightly harder to guess than "abc"'
],
suggestions: [
// 単語や文字列の繰り返しは避けましょう
'Avoid repeated words and characters'
]
}
{
// abcや6543のような連続は簡単に推測できます
warning: "Sequences like abc or 6543 are easy to guess",
// 連続は避けましょう
suggestions: ['Avoid sequences']
}
{
// 最近の年号は推測されやすいです
warning: "Recent years are easy to guess",
suggestions: [
// 最近の年号は避けましょう
'Avoid recent years',
// あなたに関係する年号は避けましょう
'Avoid years that are associated with you'
]
}
{
// 日付はよく簡単に推測されます
warning: "Dates are often easy to guess",
// あなたに関係する日付や年号は避けましょう
suggestions: 'Avoid dates and years that are associated with you'
}
warning:[
// これは一般的に使われるパスワードのトップ10です
'This is a top-10 common password',
// これは一般的に使われるパスワードのトップ100です
'This is a top-100 common password',
// これはとても一般的に使われるパスワードです
'This is a very common password',
// これは一般的に使われるパスワードに似ています
'This is similar to a commonly used password',
// 単語単体は簡単に推測されます
'A word by itself is easy to guess',
// 名前と苗字だけでは、それ自体が推測しやすいです
'Names and surnames by themselves are easy to guess',
// 一般的な名前と名字は推測しやすいです
'Common names and surnames are easy to guess'
];
suggestions: [
// 大文字はあまり役に立たない
"Capitalization doesn't help very much",
// すべての大文字は、すべての小文字と同じくらい簡単に推測することができます
"All-uppercase is almost as easy to guess as all-lowercase",
// 逆転した単語は推測するのはさほど難しくない
"Reversed words aren't much harder to guess",
//「a」 の代わりに「@」のような予測可能な代用はあまり役に立ちません
"Predictable substitutions like '@' instead of 'a' don't help very much"
];
result.sequence
また、result.sequence
には、判定にどういう処理したかという過程内容が、Object配列で入ってきます。一つ以上、返ってくる可能性がありますので、ここを取得して処理する際には注意が必要です。
以上です。