暗号学的に安全なランダムパスワード生成

暗号学的に安全な乱数生成器(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の仕組み

暗号学的に安全な擬似乱数生成器は以下を組み合わせます:

  1. エントロピープール — ハードウェアソースからランダム性を収集
  2. シード抽出 — エントロピープールから高品質なシードを導出
  3. 出力生成 — 暗号アルゴリズム(例:ChaCha20、AES-CTR)を使用してランダムバイトを生成
  4. 再シード — 新しいエントロピーからシードを定期的にリフレッシュ

一般的な落とし穴

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を使用する必要がある理由と、文字セットと長さが適切に見える場合でも生成されたパスワードを弱める微妙なバイアスを避ける方法を説明します。

試してみる — Password Generator

フルツールを開く