《PHP8上級試験対策》uniqid()とmt_rand()は推測困難なトークンに使えない理由

  • URLをコピーしました!

はじめに

PHP8上級認定試験の模擬問題で勉強している際「CSRFトークンの生成」について、uniqid()やmt_rand()の使い方につい考える機会がありました。これらの関数は便利ですが、実際には「推測困難なトークン」には使えません。本記事では、その理由と正しいアプローチを整理しておきます。

キーワード:uniqid / mt_rand / CSRF / トークン / random_bytes

目次

問題文のポイント

  • CSRF等で必要な「推測困難なトークン」
  • uniqid() を使う
  • mt_rand() と組み合わせる

一見すると「ユニークで安全そう」に見えるが…

uniqid() の性質

PHPマニュアルに記載のシグネチャ:

uniqid(string $prefix = "", bool $more_entropy = false): string
  • マイクロ秒単位の時刻を元にIDを生成
  • prefix を指定すれば接頭辞を追加できる(より一意性を高めることができる)
  • more_entropy = true にすると小数点付きでより一意性が高まる
  • 暗号学的に安全ではない(マニュアルにも明記)
  • 用途は「一意なID生成」であり「推測困難性の確保」ではない
  • 現在時刻を元にしているため、複数のサーバでマイクロ秒まで同じ時刻で、uniqidを実行すると同じ値が生成されてしまうので注意。

画像やファイルのアップロード時で、アップロードファイルを保存する際のファイル名の生成などで利用。

mt_rand() の性質

PHPマニュアルに記載のシグネチャ:

mt_rand(int $min = 0, int $max = PHP_INT_MAX): int
  • メルセンヌ・ツイスタを使った擬似乱数生成器
  • 高速で品質は高いが暗号的には安全でない
  • シードがわかれば次の出力が予測可能
  • したがってセキュリティ用途には不適

ウェブサイトのID生成、シミュレーション、ゲームなど、予測困難な数値を生成する必要がある場面で広く利用。

問題のコード

var_dump(uniqid((string)mt_rand(), true));

実行できるし結果も変わるが、推測困難性は確保できない

正しいアプローチ

$token = bin2hex(random_bytes(32)); // 64文字の推測困難トークン

これが CSRF トークン等で推奨される方法。

random_bytes(32) が生み出すもの

  • 32バイト = 256ビット のランダムデータ
  • OS が提供する 暗号学的に安全な乱数ソース (CSPRNG) を利用
  • キーボード入力、マウス動作、システムイベントなど、予測できないノイズを元に生成

bin2hex() で文字列化

  • 1バイト = 8ビット → 16進数で2桁表現
  • 32バイト → 64文字の16進数文字列に変換
"9f2c1e3ab7c4d8...7be12f"

なぜ推測困難なのか

情報量が膨大

  • 256ビットの組み合わせ数 = 2^256 ≒ 1.16 × 10^77
  • 宇宙の原子の数よりも桁違いに多い

安全な乱数ソース

  • uniqid() や mt_rand() のように「時刻」や「シード」に依存しない
  • 外部から予測することは不可能

まとめ

  • uniqid() は「一意性」用であり、「推測困難性」には不向き
  • mt_rand() も暗号学的に安全ではない
  • CSRF等には random_bytes() / random_int() を使うのが正解

この記事が気に入ったら
いいねしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次