現代のコンピュータシステムでは、乱数はパスワードの生成から単純な宝くじゲームのプレイまで、至る所で使用されています。しかし、暗号の分野では、乱数のセキュリティは単に低確率で「宝くじに当たる」ことではなく、システムが攻撃に耐えられるかどうかを決定する重要な要素です。
もし乱数生成器が安全でない場合、以下のような問題が発生する可能性があります:
- 暗号鍵の予測により、システム通信が容易に破られる;
- セッショントークンの偽造により、ユーザーアカウントの不正な乗っ取りが発生する;
- 署名アルゴリズムの失敗により、デジタル署名が法的効力を失う。
したがって、暗号アプリケーションでは、「見かけ上ランダム」な数列だけでなく、予測不可能で安全な乱数が必要です。ここで、暗号的に安全な疑似乱数生成器 (CSPRNG) が登場します。
CSPRNGとは?
CSPRNGは、暗号アプリケーション向けに設計された特別な形式の疑似乱数生成器 (PRNG) であり、より高いセキュリティ基準を持っています。
CSPRNGの主な特徴は以下の通りです:
- 予測抵抗性: 攻撃者は既知の部分に基づいて乱数列の他の部分を予測できない。
- 後方非追跡性: たとえ内部状態が攻撃者に盗まれても、以前に生成された乱数列を復元することはできない。
CSPRNGの重要性を理解するために、他の乱数生成方法と比較してみましょう。
乱数生成器の比較
-
真の乱数生成器 (TRNG)
TRNGは物理現象(例:ハードウェアノイズ、電磁干渉)に基づいて完全にランダムな値を生成しますが、速度が遅く、ハードウェアに強く依存します。
-
疑似乱数生成器 (PRNG)
PRNGはアルゴリズムとシードに基づいて乱数を生成しますが、効率的である一方で予測可能であり、暗号シナリオには不適です。
-
CSPRNG
CSPRNGは暗号原則に基づいて設計されており、PRNGのセキュリティ脆弱性に対処し、生成された乱数列が予測不可能であることを保証します。
例えば、PRNGは詩を自動生成するプログラムのようなもので、開始文(シード)がわかれば、攻撃者は後続の文を予測できます。対照的に、CSPRNGは詩のソースコードをロックするプログラムのようなもので、外部から次の文を予測することはほぼ不可能です。
CSPRNGの原理とアルゴリズム
一般的なCSPRNGアルゴリズム
-
HMAC決定論的ランダムビット生成器 (HMAC-DRBG)
HMAC (Hash-based Message Authentication Code) アルゴリズムに基づくCSPRNGで、キーと疑似乱数値を使用して内部状態を更新します。HMAC-DRBGは、二重状態管理(キー
K
と疑似乱数値V
)を通じて予測不可能性と後方非追跡性を向上させます。 -
SHA-256ベースのPRNG
SHA-256ハッシュアルゴリズムに基づく乱数生成器で、追加のキーを必要とせず、状態値のみに依存します。その更新メカニズムはシンプルですが、HMAC-DRBGほど攻撃に対する耐性はありません。
-
カウンターモード決定論的ランダムビット生成器 (CTR-DRBG)
ブロック暗号モード(AESなど)に基づく乱数生成器で、カウンターモード (CTR) を使用して乱数を生成します。高性能かつ高セキュリティが要求されるシナリオに適しており、ブロック暗号アルゴリズムのセキュリティに依存し、ハードウェア実装に適しています。
ランダムシードの生成
- オペレーティングシステムの真の乱数生成器 (TRNG) を呼び出して高エントロピーのシードを取得します。
- ハードウェアセキュリティモジュール (HSM) またはシステムエントロピーソース(
/dev/urandom
など)を使用してシードを生成し、CSPRNGの初期状態のランダム性を確保します。
CSPRNGの使用方法
前述の原理から、CSPRNGの有効性を確保するためには、真のランダムシードから始めるべきであることがわかります。初期シードが真のランダム性を保証できない場合、理論的にはCSPRNGアルゴリズムのみに依存して真の暗号セキュリティを確保することは困難です。
実際、現代のオペレーティングシステムはこの問題を考慮しており、システムレベルの真の乱数インターフェースを提供しています。これらは主に、ハードウェアノイズ、キーボードやマウスのイベントなどの基礎的なエントロピーソースに基づいて高品質の乱数を生成します。
例えば、LinuxやmacOSの /dev/random
や /dev/urandom
インターフェース、Windowsの CryptGenRandom
や BCryptGenRandom
インターフェースなどがあります。
一般的なプログラミング言語も、システムレベルの真の乱数インターフェースのカプセル化を提供しています:
- Node.jsの
crypto.randomBytes()
メソッド; - Javaの
SecureRandom
クラス; - Pythonの
os.urandom()
インターフェースとsecrets
モジュールなど。
システムレベルの真の乱数が生成できるのに、なぜCSPRNGアルゴリズムが必要なのか疑問に思うかもしれません。
前述のように、オペレーティングシステムのエントロピープールの容量は限られており、真の乱数を生成する速度も限られています。短時間で大量の乱数が必要なシナリオでは、システムレベルの乱数では要求を満たすことができません。CSPRNGはシステムレベルの真の乱数を強化し、暗号的に安全な乱数をより高速に生成し、より高いカスタマイズ性とより多くのランダムビットを提供します。
CSPRNGを使用する際には、以下の点に注意する必要があります:
- 一般的な疑似乱数シードを使用すると、攻撃者が乱数列を予測し、CSPRNGの出力が安全でなくなる可能性があります。前述のシステムレベルのインターフェースを使用して、真の乱数をシードとして生成できます。
- CSPRNGの内部状態が漏洩すると、生成された乱数が予測される可能性があります。このような状況を避けるために、CSPRNGの状態やシードを定期的に更新することができます。
アプリケーション例
鍵生成
暗号化通信では、鍵のセキュリティがシステムのセキュリティを直接決定します。鍵が予測可能である場合、攻撃者はブルートフォース攻撃や鍵生成パターンの解析を通じて鍵を取得し、通信内容を解読することができます。したがって、鍵生成には非常に高い予測不可能性を持つ乱数が必要であり、CSPRNGの特徴(予測抵抗性と後方非追跡性)は生成された鍵が予測困難であることを保証します。
セッショントークン
Webアプリケーションでは、セッショントークンはユーザーセッション(ログイン状態など)を識別するために使用されます。トークンが攻撃者に推測または偽造されると、セッションハイジャック(Session Hijacking)が発生し、ユーザーになりすまして操作を行う可能性があります。
例えば、CSRFやPKCEでは、ランダムな state
や code
を使用してセッションやプロセス攻撃を防止します。
他にも、DBに書き込むパスワードをハッシュアルゴリズムで処理する際に、単純なハッシュアルゴリズムだけではレインボーテーブル攻撃に対抗するのが難しい場合があります。このような場合、ハッシュアルゴリズムを使用する際に、Saltというランダム変数を追加して、同じパスワードが異なるハッシュ値を生成するようにします。