randとrandom_intの決定的な違い

random_intは、OSの暗号論的に安全な乱数源から整数を生成する関数です。予測可能なmt_randと違い、トークンやPINなどセキュリティが絡む乱数を安全に作れます。

rand / mt_rand は予測できてしまう

rand()mt_rand() は高速で手軽ですが、疑似乱数生成器(PRNG)なので、シードや過去の出力から次の値を予測できる性質があります。ゲームのダミーデータや表示のシャッフルなら何の問題もありませんが、これでパスワードリセットのトークンやセッション ID を作るのは危険です。

// 統計的な用途なら十分
$dice = mt_rand(1, 6);

// これはダメ: 予測可能なトークン
$token = md5(mt_rand()); // 弱い

「乱数だから当てられないだろう」という感覚が一番の落とし穴で、PRNG の出力はアルゴリズム次第で復元可能だと思っておくべきですね。

セキュアな用途はrandom_int

推測されては困る乱数、つまりセキュリティに関わる値には random_int() を使います。OS が提供する暗号論的に安全な乱数源から生成するため、予測が現実的に不可能です。範囲指定の使い勝手は mt_rand() とほぼ同じなので、乗り換えの負担も小さいです。

// 暗号論的に安全な整数乱数
$pin = random_int(0, 999999);

// 適切な乱数源が無ければ Exception を投げる = 静かに弱くならない
$code = str_pad((string)random_int(0, 999999), 6, '0', STR_PAD_LEFT);

安全な乱数源を確保できないと例外を投げてくれるのも安心材料です。黙って弱い乱数にフォールバックしない、という設計がありがたいですね。

バイト列やトークンはrandom_bytes

整数ではなくトークン文字列が欲しいときは random_bytes() でランダムなバイト列を作り、bin2hex() で16進文字列にするのが定番です。これもセキュアな乱数源を使うので、CSRF トークンや API キーの生成に安心して使えます。

// 32バイト = 64文字の16進トークン
$token = bin2hex(random_bytes(32));

// URL セーフにしたいなら base64 を加工しても良い
$safe = rtrim(strtr(base64_encode(random_bytes(24)), '+/', '-_'), '=');

まとめ

乱数は「予測されて困るかどうか」で選ぶのが分かれ目です。表示のシャッフルや統計用途なら速い mt_rand() で十分ですが、トークン・PIN・セッション ID のようにセキュリティが絡むものは必ず random_int()random_bytes() を使う。この線引きさえ守っておけば、乱数由来のセキュリティ事故はほぼ防げると思います。

よくある質問

Q. mt_randは使ってはいけないのですか。
A. 表示のシャッフルや統計用途なら問題ありません。予測されて困る値(トークンやセッションID)にだけ使わないようにします。

Q. ランダムな文字列トークンが欲しいときは。
A. random_bytes() でバイト列を作り bin2hex() で16進文字列にするのが定番です。

Q. random_intが例外を投げることはありますか。
A. 安全な乱数源を確保できないと例外を投げます。黙って弱い乱数にフォールバックしない設計です。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です