暗号学的に安全なランダムパスワード生成
暗号学的に安全な乱数生成器(CSPRNG)の仕組みとパスワード生成に不可欠な理由を理解します。Web Crypto API、エントロピーソース、一般的な落とし穴を解説します。
Advanced
詳細な説明
暗号学的ランダム性が重要な理由
パスワードはそのランダム性によってのみ強力です。JavaScriptのMath.random()や同様の非暗号PRNGを使用すると、ランダムに見えるが実際には予測可能なパスワードが生成されます。アルゴリズムを知りシードを推定できる攻撃者は、出力シーケンス全体を再現できます。
Math.random() vs crypto.getRandomValues()
Math.random()(パスワード生成に安全ではない)
// パスワード生成に使用しないでください
Math.random().toString(36).slice(2);
問題点:
- 決定論的PRNGを使用(V8では通常xorshift128+)
- 低エントロピーソースからシード(システム時間)
- いくつかの出力から状態を復元可能
- セキュリティアプリケーション向けに設計されていない
crypto.getRandomValues()(安全)
// 正しいアプローチ
const array = new Uint8Array(16);
crypto.getRandomValues(array);
特性:
- オペレーティングシステムのCSPRNGを使用(WindowsのCryptGenRandom、Linux/macOSの/dev/urandom)
- ハードウェアエントロピーからシード(CPUタイミング、割り込みジッタ、ハードウェアRNG)
- 出力は真のランダム性と計算上区別不可能
- Web Crypto APIで標準化(すべてのモダンブラウザで利用可能)
CSPRNGの仕組み
暗号学的に安全な擬似乱数生成器は以下を組み合わせます:
- エントロピープール — ハードウェアソースからランダム性を収集
- シード抽出 — エントロピープールから高品質なシードを導出
- 出力生成 — 暗号アルゴリズム(例:ChaCha20、AES-CTR)を使用してランダムバイトを生成
- 再シード — 新しいエントロピーからシードを定期的にリフレッシュ
一般的な落とし穴
1. モジュロバイアス
// バイアスあり:一部の文字がわずかに多く出現
const index = randomByte % 62;
// バイアスなし:棄却サンプリング
function unbiasedRandom(max) {
const limit = 256 - (256 % max);
let value;
do {
const array = new Uint8Array(1);
crypto.getRandomValues(array);
value = array[0];
} while (value >= limit);
return value % max;
}
ランダム範囲(バイトの場合256)が文字セットサイズで均等に割り切れない場合、一部の出力がわずかに高い確率を持ちます。棄却サンプリングはこのバイアスを排除します。
2. 不十分なエントロピー収集
一部のシステムは低エントロピーで開始します(組み込みデバイス、起動時のVM)。パスワード生成はエントロピーの利用可能性を検証するか、十分なエントロピーが収集されるまでブロックすべきです。
3. 予測可能なシード
タイムスタンプ、プロセスID、またはその他の推測可能な値をシードとして使用すると、生成器全体が損なわれます。
ブラウザサポート
Web Crypto API(crypto.getRandomValues())は以下でサポートされています:
- すべてのモダンブラウザ(Chrome、Firefox、Safari、Edge)
- Node.js(
crypto.randomBytes()) - Deno(
crypto.getRandomValues()) - Web WorkerとService Worker
ランダム性の検証
真のランダム性は証明できませんが、統計テストで明らかなパターンを検出できます:
- NIST SP 800-22 — 乱数生成器の統計テストスイート
- Diehardテスト — 統計テストのバッテリー
- TestU01 — 包括的なPRNGテストフレームワーク
ユースケース
暗号学的ランダム性を理解することは、パスワード生成器、トークンシステム、暗号アプリケーションを構築するすべての開発者にとって不可欠です。Math.random()の代わりにWeb Crypto APIを使用する必要がある理由と、文字セットと長さが適切に見える場合でも生成されたパスワードを弱める微妙なバイアスを避ける方法を説明します。