24
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

パスワード強度推定メーター(zxcvbn.js)を実装してみる

Last updated at Posted at 2020-10-29

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

使い方

お使いのウェブページ内のソース部分に以下を追加しまます。

index.html
<script src="js/zxcvbn.js"></script>

書き方は、

zxcvbn(password, user_inputs=[])

となります。第一引数に「パスワード文字列」、第二引数には「追加辞書」を指定します。実際のコードにしてみたのが、以下です。

index.html
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配列で入ってきます。一つ以上、返ってくる可能性がありますので、ここを取得して処理する際には注意が必要です。

以上です。

24
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?