乱数の生成方法!PRNGとCSPRNGの「違い」と「使い分け」を解説!

Webアプリやゲーム開発、セッションIDの生成など、「乱数」はさまざまなシーンで使われています。

プログラミングでは複数の乱数生成方法があり、用途に応じて使い分けることが大切です。

この記事では『乱数の生成方法』について、以下の内容をシンプルなサンプルコードを用いてわかりやすく解説します。

  • 乱数の生成方法
  • 疑似乱数生成器(PRNG)とは?
    • 疑似乱数生成器(PRNG)の例
  • 暗号論的疑似乱数生成器(CSPRNG)とは?
    • 暗号論的疑似乱数生成器(CSPRNG)の例
  • 疑似乱数生成器(PRNG)と暗号論的疑似乱数生成器(CSPRNG)の使い分け

乱数の生成方法

乱数の生成方法

乱数とは、サイコロの目のように、どの値が出るかをランダムに決めるための数です。プログラミングでは、主に一般的な疑似乱数生成器(PRNG)と、セキュリティ用途に適した暗号論的疑似乱数生成器(CSPRNG)を使い分けます。

  • 疑似乱数生成器(PRNG)
    • アルゴリズムによって計算された再現性のある乱数を生成する。
    • JavaScriptでは、Math.random()で一般的なPRNGによる乱数を取得できます。
    • Pythonでは、標準ライブラリのrandomモジュールのrandom.random()などで、一般的なPRNGによる乱数を取得できます。
    • PRNGは「Pseudo-Random Number Generator」の略です。
  • 暗号論的疑似乱数生成器(CSPRNG)
    • 予測困難な高品質の乱数を生成する。
    • JavaScriptでは、crypto.getRandomValues()で暗号論的に安全な乱数を取得できます。
    • Pythonでは、secretsモジュールやos.urandom()で暗号論的に安全な乱数を取得できます。
    • CSPRNGは「Cryptographically Secure Pseudo-Random Number Generator」の略です。

なお、CSPRNGもPRNGの一種です。この記事では、暗号用途に適さない一般的なPRNGを「PRNG」、暗号用途に適したPRNGを「CSPRNG」と表記します。

補足

他にも乱数を生成するものとして「真性乱数生成器(TRNG: True Random Number Generator)」があります。これは、自然界の物理現象(熱雑音、大気ノイズ、放射性崩壊など)を利用して乱数を生成するものです。ハードウェアが必要なのでソフトウェアだけでは真性乱数生成器で乱数を生成することはできません。

疑似乱数生成器(PRNG)とは?

疑似乱数生成器(PRNG)は、アルゴリズムによって計算された再現性のある乱数を生成します。完全にランダムではなく、決まった手順に基づいて生成されるため、同じ生成方式・シード・処理順で実行すると、同じ乱数列を再現できるという特徴があります。一般的なPRNGは高速かつ軽量ですが、攻撃者による予測を防ぐことを目的に設計されていないため、セキュリティ用途には不向きです。

なお、シードとは、乱数を作る計算の出発点となる値です。同じ生成方式で、同じシードから同じ順番で処理すると、同じ数の並びを再現できます。

疑似乱数生成器(PRNG)の特徴

  • 高速かつ軽量である
  • 同じ生成方式・シード・処理順で実行すると、同じ乱数列を再現できる
  • セキュリティ用途には向かない(予測される可能性があるため)

疑似乱数生成器(PRNG)を使ってはいけないケース

以下のようなセキュリティが関わる処理では、疑似乱数生成器(PRNG)は使わないでください。

  • ワンタイムパスワード(OTP)
  • 暗号鍵の生成
  • セッションID・アクセストークンの生成

これらに疑似乱数生成器(PRNG)を使うと、悪意ある攻撃者に予測されるリスクがあります。

疑似乱数生成器(PRNG)の例

JavaScriptにおける疑似乱数生成器(PRNG)

JavaScriptでは、Math.random()で一般的なPRNGによる乱数を取得できます。次の例では、0以上1未満の小数を1つ生成して表示します。

const r = Math.random();
console.log(r); // 例: 0.4123456789

なお、Math.random()には、利用者がシードを指定する標準機能はありません。

Pythonにおける疑似乱数生成器(PRNG)

Pythonでは、標準ライブラリのrandomモジュールのrandom.random()などで、一般的なPRNGによる乱数を取得できます。次の例では、0以上1未満の小数を1つ生成して表示します。

import random

r = random.random()
print(r)  # 例: 0.7423827611191788

Pythonでは、random.seed(123)のようにシードを指定できます。同じ生成方式・シード・処理順で実行すれば同じ乱数列を再現できるため、テストやシミュレーションに便利です。

暗号論的疑似乱数生成器(CSPRNG)とは?

暗号論的疑似乱数生成器(CSPRNG)は、予測困難な高品質の乱数を生成します。セキュリティが求められる場面では、こちらの使用が推奨されます。

暗号論的疑似乱数生成器(CSPRNG)の特徴

  • 予測困難な高品質な乱数を生成する
  • セッションIDやトークンなどに適している
  • 安全な乱数を必要とする暗号化処理に向いている

暗号論的疑似乱数生成器(CSPRNG)の例

JavaScriptにおける暗号論的疑似乱数生成器(CSPRNG)

JavaScriptでは、crypto.getRandomValues()で暗号論的に安全な乱数を取得できます。crypto.getRandomValues()はブラウザ組み込みの安全な乱数生成関数です。次の例では、整数を1つ格納できる配列を用意し、0〜4,294,967,295の乱数を書き込んで表示します。

const arr = new Uint32Array(1);
crypto.getRandomValues(arr);
console.log(arr[0]); // セキュアな乱数(整数)

Pythonにおける暗号論的疑似乱数生成器(CSPRNG)

Pythonでは、secretsモジュールやos.urandom()で暗号論的に安全な乱数を取得できます。secretsは暗号論的に安全な乱数を提供するために設計されており、パスワード生成やセッションIDの作成などに最適です。os.urandom()はランダムなバイト列を返す関数で、暗号用途でも使用できます。

次の例では、secrets.randbelow(1000)で0〜999の整数を1つ生成して表示します。

import secrets

r = secrets.randbelow(1000)
print(r)  # 0〜999 のセキュアな乱数

次の例では、os.urandom(4)でランダムな4バイトを取得し、int.from_bytes()で整数に変換して表示します。'big'は、先頭のバイトを上位の桁として扱う指定です。

import os

r = int.from_bytes(os.urandom(4), 'big')
print(r)  # セキュアな乱数(整数)

注意点

これらは整数の生成例です。安全な乱数生成方法を使っていても、候補が少ない値は推測されやすいため、セッションIDやトークンには十分な長さが必要です。

疑似乱数生成器(PRNG)と暗号論的疑似乱数生成器(CSPRNG)の使い分け

乱数生成器は、生成した値を第三者に予測されると困るかどうかで使い分けます。ゲームの演出、シミュレーション、テストデータの生成など、セキュリティに関わらない用途では、一般的なPRNGを利用できます。一方、以下のような用途では、CSPRNGを使います。

  • セッションID・アクセストークン・パスワードリセット用トークンの生成
  • ランダムなパスワードや認証コードの生成
  • 暗号鍵や、鍵交換で使う秘密の値の生成

これらのケースでは、予測困難で安全な乱数を使わないと、情報漏洩やなりすましのリスクが生じる可能性があります。

なぜ疑似乱数生成器(PRNG)はセキュリティに不向きなのか?

一般的なPRNGは、攻撃者による予測を防ぐことを目的に設計されていないため、次のような問題が起こり得ます。

  • 生成方式によっては、出力された値から内部の状態や次の値を推測される可能性がある
  • 時刻などの推測しやすい値をシードにすると、乱数列を予測される可能性がある

暗号処理では「推測されないこと」が重要です。一般的なPRNGでシードだけを工夫しても、暗号用途の安全性は保証されません。セキュリティが関わる値の生成には、CSPRNGを使いましょう。

本記事のまとめ

この記事では『乱数の生成方法』について、以下の内容を説明しました。

  • 疑似乱数生成器(PRNG)
    • アルゴリズムに基づいた再現性のある乱数
    • 高速かつ軽量で処理性能に優れる
    • 同じ生成方式・シード・処理順で実行すると、同じ乱数列を再現できる
    • JavaScript: Math.random()
    • Python: random.random()
  • 暗号論的疑似乱数生成器(CSPRNG)
    • 予測困難な高品質な乱数
    • セキュリティが求められる場面で必須
    • JavaScript: crypto.getRandomValues()
    • Python: secrets.randbelow(), os.urandom()

お読みいただきありがとうございました。

スポンサーリンク