Cryptographic Randomness in Password Generation
About 8 min read
パスワード生成ツールの安全性を左右する最も重要な要素は、使用される乱数の品質です。 見た目がランダムに見える文字列でも、生成に使われた乱数が予測可能であれば、 攻撃者はパスワードを推測できてしまいます。NIST SP 800-90A は 暗号学的乱数生成器の要件を定めた標準規格であり、パスワード生成に使用する 乱数はこの基準を満たす必要があります。2025 年現在、量子コンピュータの 発展に伴い、NIST は耐量子暗号 (PQC) の標準化を進めており、 乱数生成の安全性基準も将来的に見直される可能性があります。 本記事では、乱数の種類と品質の違いを 技術的に解説し、パスつく.com が採用する安全な乱数生成方式について説明します。パスワードのエントロピーの 概念と合わせて理解することで、パスワード強度の本質が見えてきます。
Math.random() の危険性
JavaScript で最も手軽に乱数を生成する方法は `Math.random()` です。 しかし、この関数はパスワード生成には絶対に使用してはいけません。 `Math.random()` は疑似乱数生成器 (PRNG: Pseudo-Random Number Generator) であり、内部状態 (シード値) から決定論的にアルゴリズムで数列を生成します。
`Math.random()` の具体的な問題点は以下のとおりです。
- 内部状態が推測可能: 出力された乱数列から内部状態を逆算できる場合がある
- 暗号学的安全性の保証がない: ECMAScript 仕様は乱数の品質について何も保証していない
- ブラウザごとに実装が異なる: V8 エンジン (Chrome) では xorshift128+ アルゴリズムが使われているが、これは暗号用途には不適切
- シード値の予測: 一部の実装ではタイムスタンプがシードに使われるため、生成時刻が分かれば出力を再現できる可能性がある
実際に、セキュリティ研究者によって `Math.random()` の出力から内部状態を 復元する攻撃手法が実証されています。2015 年に公開された研究では、 V8 エンジンの xorshift128+ 実装に対して、わずか 64 個の連続した出力値から 内部状態を完全に復元できることが示されました。`Math.random()` で 生成されたパスワードは、見た目はランダムでも、 攻撃者にとっては予測可能な文字列にすぎません。
見落としがちな注意点として、`Math.random()` の出力範囲は 0 以上 1 未満の浮動小数点数であり、内部的には 52 ビットの仮数部しか 使用しません。つまり、出力可能な値の種類は最大で 2 の 52 乗 (約 4.5 × 10 の 15 乗) 個に限られます。暗号学的な用途では 128 ビット以上のエントロピーが求められるため、 `Math.random()` 単体では根本的にビット数が不足しています。
CSPRNG の仕組み
パスワード生成に適した乱数は、暗号学的擬似乱数生成器 (CSPRNG: Cryptographically Secure Pseudo-Random Number Generator) で生成する必要があります。CSPRNG は以下の 2 つの性質を満たします。
- 予測不可能性: 過去の出力をすべて知っていても、次の出力を予測できない
- 逆算不可能性: 出力から内部状態を逆算することが計算量的に不可能
CSPRNG は、オペレーティングシステムが収集するエントロピー源を シードとして使用します。エントロピー源には、ハードウェアの電気的ノイズ、 マウスの動き、キーボード入力のタイミング、ディスク I/O のタイミングなど、 物理的に予測不可能な事象が含まれます。これらの予測不可能なデータを 暗号学的ハッシュ関数やブロック暗号で処理することで、 高品質な乱数列を生成します。
Linux では `/dev/urandom`、Windows では BCryptGenRandom が OS レベルの CSPRNG として提供されています。 これらは長年にわたるセキュリティ監査と実績を持つ、信頼性の高い実装です。 暗号学的乱数生成の理論的背景について、CSPRNG and cryptographic security design books (Amazon)も参考になります。
Web Crypto API: crypto.getRandomValues()
ブラウザ環境で CSPRNG を利用するための標準的な方法が、 Web Crypto API の `crypto.getRandomValues()` です。 この API は W3C によって標準化されており、 すべてのモダンブラウザで利用可能です。
`crypto.getRandomValues()` は、OS が提供する CSPRNG を直接呼び出して 乱数を生成します。つまり、ブラウザ独自のアルゴリズムではなく、 OS レベルで検証された暗号学的に安全な乱数を取得できます。
`Math.random()` との決定的な違いは、`crypto.getRandomValues()` が 暗号学的安全性を保証している点です。出力から内部状態を推測することは 計算量的に不可能であり、過去の出力を知っていても 将来の出力を予測することはできません。
ただし、`crypto.getRandomValues()` にも制約があります。 1 回の呼び出しで取得できるバイト数は最大 65,536 バイトに制限されており、 これを超えるリクエストは例外をスローします。パスワード生成の用途では この制限に達することはまずありませんが、大量の乱数を必要とする シミュレーション用途などでは注意が必要です。
乱数品質の検証手法として、NIST SP 800-22 が定める統計的テストスイートが 広く使われています。このテストスイートには周波数テスト、ランテスト、 線形複雑度テストなど 15 種類の検定が含まれており、 `crypto.getRandomValues()` の出力はこれらすべてに合格します。一方、 `Math.random()` の出力は複数の検定で不合格となることが知られています。 乱数品質の評価方法について、NIST randomness testing and cryptographic evaluation books (Amazon)も参考になります。
パスつく.com の乱数生成方式
パスつく.com は、パスワード生成のすべての工程で `crypto.getRandomValues()` を 使用しています。`Math.random()` は一切使用していません。 これにより、生成されるパスワードの各文字が暗号学的に安全な乱数に 基づいて選択されることが保証されます。
均一分布の保証
乱数の品質に加えて、パスつく.com は文字選択の均一性にも配慮しています。 `crypto.getRandomValues()` が返す値を文字セットのサイズで単純に 剰余演算すると、文字セットのサイズが 2 のべき乗でない場合に わずかな偏りが生じます。たとえば、0-255 の範囲の乱数を 62 種類の 文字セット (英大小文字 + 数字) で剰余演算すると、先頭の 8 文字が 他の文字より約 1.6% 高い確率で選ばれてしまいます。 パスつく.com ではこの偏りを排除するために、 リジェクションサンプリング (棄却法) を採用し、 すべての文字が完全に等確率で選択されるようにしています。
ブラウザ内完結の安全性
パスつく.com の乱数生成とパスワード生成はすべてブラウザ内で完結します。 生成されたパスワードや乱数のシード値がネットワークを通じて 外部に送信されることはありません。サーバーサイドの処理を介さないため、 通信経路上での傍受リスクもゼロです。
パスワード生成ツールを選ぶ際は、どのような乱数生成方式を採用しているかを 確認することが重要です。`Math.random()` を使用しているツールは セキュリティ上の懸念があるため、`crypto.getRandomValues()` を使用している パスつく.com のようなツールを選択してください。
乱数品質のセルフチェックリスト
パスワード生成ツールを選ぶ際に、以下の観点で乱数品質を確認してください。
- ツールが `crypto.getRandomValues()` または同等の CSPRNG を使用しているか
- `Math.random()` を使用していないことが明示されているか
- 生成処理がブラウザ内 (クライアントサイド) で完結しているか
- 文字選択にリジェクションサンプリングなどの偏り排除手法を採用しているか
- ソースコードが公開されており、乱数生成方式を検証できるか
パスつく.com はこれらすべての条件を満たしています。エントロピーの概念を理解し、ハッシュ関数との関係を把握することで、 乱数品質がパスワード強度にどう影響するかをより深く理解できます。
今すぐできること
- パスつく.com で 16 文字以上のパスワードを生成し、現在使用中の「自分で考えた」パスワードを置き換える
- 利用中のパスワード生成ツールが crypto.getRandomValues() (または同等の CSPRNG) を使用しているか確認する
- パスつく.com の強度メーターで、生成したパスワードが 80 ビット以上のエントロピーを持つことを確認する
- ブラウザを最新バージョンに更新し、Web Crypto API の最新実装を利用できる状態にする