パスワード生成アルゴリズムの仕組み|乱数とCSPRNG
安全なパスワードを自動生成する仕組みの核心は「予測できない乱数」と「偏りのない文字選択」の2点にあります。パスワード生成 アルゴリズムでは、一般的なプログラムが使う擬似乱数(PRNG)ではなく、暗号論的に安全な擬似乱数生成器(CSPRNG)を使うことが決定的に重要です。
ブラウザ上のパスワード生成ツールで「生成」ボタンを押すと一瞬で文字列が現れますが、その裏では乱数源の選び方と文字の割り当て方が安全性を大きく左右しています。本記事では、パスワード生成 アルゴリズムがどのように動いているのかを、乱数の種類・偏りの排除・エントロピーという3つの観点から解説します。
擬似乱数(PRNG)と暗号論的乱数(CSPRNG)の違い
コンピュータが生成する乱数のほとんどは、数式で次の値を計算する擬似乱数(PRNG)です。代表例が線形合同法やメルセンヌ・ツイスタで、シミュレーションやゲームには十分ですが、内部状態が推測されると以降の出力がすべて予測できてしまいます。パスワードの生成にこれを使うと、生成器の状態を割り出した攻撃者に候補を絞り込まれる危険があります。
一方のCSPRNG(暗号論的擬似乱数生成器)は、過去の出力から次の出力を現実的な計算量で予測できないよう設計されています。OS が集めたエントロピー(マウス操作、割り込みタイミング、ハードウェア乱数など)を種として利用する点も特徴です。実装としては次のようなAPIが標準的に使われます。
- ブラウザ(JavaScript): crypto.getRandomValues() や crypto.randomUUID()
- Node.js: crypto.randomInt() / crypto.randomBytes()
- Python: secrets モジュール(random モジュールは不可)
- OS: Linux の /dev/urandom、Windows の BCryptGenRandom
逆に、多くの言語で手軽に呼べる Math.random() や Python の random.random() は擬似乱数であり、パスワード用途には使ってはいけません。この選択こそが、安全なパスワード生成 アルゴリズムの土台です。仕組み全体の流れはパスワードがどう生成されるかの記事でも触れています。
偏りのない文字選択がなぜ難しいのか
安全な乱数が手に入っても、それを文字に割り当てる段階で偏りが生まれることがあります。よくある失敗が、乱数を文字集合の大きさで割った余り(剰余)を使う方法です。
たとえば0〜255の値を返す乱数を、62種類の文字(英大文字・小文字・数字)に 余り演算(mod 62) で割り当てると、256は62で割り切れないため、若い番号の文字がわずかに多く選ばれます。このモジュロ・バイアスは1文字あたりの偏りは小さくても、桁数が増えると無視できません。
これを防ぐ代表的な手法が棄却サンプリング(rejection sampling)です。文字数の倍数に収まらない範囲の乱数値が出たら、それを捨てて引き直すことで、すべての文字が等確率で選ばれるようにします。前述の crypto.randomInt() や Python の secrets.choice() は内部でこの偏り対策を行っているため、自前で剰余計算をするより安全です。
エントロピーで強度を測る
パスワードの強度は、長さと文字種から計算されるエントロピー(bit)で見積もれます。1文字あたりのエントロピーは、文字集合の大きさ n に対して log2(n) ビットです。実際に計算すると次のようになります。
- 英数字62種類: 1文字あたり 約5.95ビット(log2(62))
- 記号を含む94種類: 1文字あたり 約6.55ビット(log2(94))
- 62種類×16文字: 合計 約95.27ビット
- 94種類×16文字: 合計 約104.87ビット
この数値は「62の16乗」や「94の16乗」の総当たり手間を2進数で表したものです。一般に80ビットを超えれば現実的な総当たりに強いとされ、文字種を増やすより長さを伸ばす方がエントロピーを効率よく稼げます。ただしこの計算は、乱数が真に均一である前提に立ちます。PRNGやモジュロ・バイアスがあると、見かけのエントロピーは実際の安全性を上回ってしまいます。
良い生成アルゴリズムに共通する設計
ここまでを踏まえると、信頼できるパスワード生成 アルゴリズムには共通する設計原則が見えてきます。
- CSPRNGを乱数源にする: OS由来のエントロピーを使い、擬似乱数を避ける
- 偏りを排除する: 剰余ではなく棄却サンプリングで等確率を保証する
- 十分な長さを確保する: 記号追加より桁数増加を優先する
- 生成をクライアント側で完結させる: 生成した文字列を外部へ送信しない
- 各文字を独立に選ぶ: 前の文字に依存させず、辞書語や規則性を作らない
生成後にパスワードがどのように保管・検証されるかは別の論点で、サーバー側ではハッシュ化が前提になります。この点はパスワードハッシュの解説で詳しく扱っています。生成と保管の両輪を理解して初めて、実運用で安全なパスワード管理が成立します。
まとめ
パスワード生成 アルゴリズムの良し悪しは、派手な見た目ではなく「乱数源にCSPRNGを使っているか」「文字選択の偏りを排除しているか」という地味な2点で決まります。エントロピーの計算はあくまで理想条件での上限であり、その前提を守る実装があってこそ意味を持ちます。ツールを選ぶときは、生成がブラウザ内で完結し、標準の暗号乱数APIを使っているかを確認しましょう。
よくある質問
Math.random() で作ったパスワードは危険ですか?
はい、避けるべきです。Math.random() は擬似乱数(PRNG)であり内部状態が予測可能なため、生成されたパスワードの候補を絞り込まれる恐れがあります。ブラウザなら crypto.getRandomValues() などのCSPRNGを使う実装を選んでください。
モジュロ・バイアスとは何ですか?
乱数値を文字数で割った余りで文字を割り当てるとき、割り切れないことで一部の文字がわずかに多く選ばれる偏りのことです。棄却サンプリングで範囲外の値を引き直すことで解消でき、secrets.choice() などは内部でこの対策を行っています。
パスワードは何ビットのエントロピーがあれば安全ですか?
一般に80ビットを超えると現実的な総当たり攻撃に強いとされます。英数字62種類なら1文字約5.95ビットなので、16文字で約95.27ビットに達します。文字種を増やすより長さを伸ばす方が効率よくエントロピーを稼げます。