PHPでランダムな値を生成する方法について

採用はこちら

PHPでランダムな値を生成する方法は、目的によって使い分ける必要があります。

たとえば、画面に表示するメッセージをランダムに切り替えるだけであれば、比較的シンプルな乱数でも問題ありません。

一方で、認証コード、パスワードリセット用トークン、CSRFトークン、APIキーなど、第三者に推測されると困る値を生成する場合は、セキュリティに配慮した方法を使う必要があります。

PHPでは主に、次のような関数やクラスを使ってランダムな値を生成できます。

方法主な用途セキュリティ用途
random_int()ランダムな整数を生成する
random_bytes()ランダムなバイト列を生成する
Random\RandomizerPHP 8.2以降のランダムAPI
mt_rand()簡易的な整数乱数を生成する×
rand()簡易的な整数乱数を生成する×
array_rand()配列からランダムにキーを取得する×
shuffle()配列をランダムに並び替える×

基本的には、セキュリティに関係するランダム値には random_int() または random_bytes() を使うと覚えておくとよいでしょう。

目次

ランダムな整数を生成する方法

random_int()を使う方法

PHPで安全なランダム整数を生成したい場合は、random_int() を使います。

<?php

$number = random_int(1, 100);

echo $number;

このコードでは、1〜100の範囲でランダムな整数を生成します。

random_int() は、暗号学的に安全なランダム整数を生成する関数です。

そのため、認証コード、招待コード、抽選番号など、推測されると困る値を作る場合に向いています。

6桁の認証コードを生成する例

メール認証やSMS認証で使う6桁のコードは、次のように生成できます。

<?php

$code = random_int(100000, 999999);

echo $code;

出力例は次の通りです。

582941

このように書くことで、100000〜999999の範囲からランダムな6桁の整数を生成できます。

ただし、認証コードを実務で使う場合は、コードを生成するだけでは不十分です。

有効期限、試行回数制限、レート制限などもあわせて設計する必要があります。

ランダムなバイト列を生成する方法

random_bytes()を使う方法

トークンやAPIキーのような、より強いランダム値を生成したい場合は random_bytes() を使います。

<?php

$bytes = random_bytes(16);

echo bin2hex($bytes);

出力例は次の通りです。

9f2c1a8d6e4b7c0f3a92d45b1c7e8a10

random_bytes(16) は、16バイトのランダムなバイト列を生成します。

ただし、そのままではバイナリデータなので、Webアプリケーションでは bin2hex() を使って16進数の文字列に変換することがよくあります。

パスワードリセット用トークンを生成する例

パスワードリセットURLに使うトークンは、次のように生成できます。

<?php

$token = bin2hex(random_bytes(32));

echo $token;

出力例は次の通りです。

f4d9c5d6e2b14a8a9d0c38f4b572ad890b8fd1f2e6aa0cc43f865c7d8f9b102e

random_bytes(32) で32バイトのランダムデータを生成し、bin2hex() で16進数文字列に変換しています。

この場合、最終的な文字列は64文字になります。

パスワードリセットURLを作る場合は、次のように使えます。

<?php

$token = bin2hex(random_bytes(32));

$resetUrl = 'https://example.com/reset-password?token=' . $token;

echo $resetUrl;

パスワードリセット、メール認証、CSRFトークン、APIキーなどには、mt_rand()rand() ではなく、random_bytes() を使うようにしましょう。

random_bytes()の例外処理

random_bytes() は、ランダム値の生成に失敗した場合に例外を投げることがあります。

通常の環境ではあまり意識する場面は多くありませんが、実務では例外処理を入れておくとより安全です。

<?php

try {
    $token = bin2hex(random_bytes(32));
    echo $token;
} catch (Random\RandomException $e) {
    error_log($e->getMessage());
    echo 'トークンの生成に失敗しました。';
}

ユーザーには詳細なエラー内容を表示せず、ログに記録して調査できるようにしておくのが基本です。

簡易的なランダム整数を生成する方法

mt_rand()を使う方法

セキュリティに関係しない簡易的な用途であれば、mt_rand() を使うこともできます。

<?php

$number = mt_rand(1, 10);

echo $number;

このコードでは、1〜10の範囲でランダムな整数を生成します。

mt_rand() は、表示内容のランダム切り替えやテストデータの生成などには使えます。

ただし、暗号学的に安全な乱数ではありません。

そのため、認証コード、トークン、パスワードリセット、景品抽選、クーポン発行など、推測や操作を避けたい用途には使わないようにしましょう。

ランダムなメッセージを表示する例

<?php

$messages = [
    '今日は良い日です',
    '焦らず進みましょう',
    '小さな改善を続けましょう',
];

$index = mt_rand(0, count($messages) - 1);

echo $messages[$index];

このように、表示するメッセージをランダムに変えるだけであれば、mt_rand() でも大きな問題はありません。

ただし、迷った場合は random_int() を使っておくと安全です。

<?php

$index = random_int(0, count($messages) - 1);

echo $messages[$index];

rand()を使う場合の注意点

rand()は新規コードでは積極的に使わない

PHPには、昔から rand() という関数もあります。

<?php

$number = rand(1, 100);

echo $number;

ただし、新しく書くコードで rand() を積極的に使う理由はあまりありません。

PHP 7.1以降では、rand()mt_rand() のエイリアスになっています。

また、rand()mt_rand() と同じく、暗号学的に安全な乱数ではありません。

セキュリティに関係する値を生成する場合は、random_int()random_bytes() を使いましょう。

rand()ではなくrandom_int()を使う例

<?php

// 推奨
$number = random_int(1, 100);

echo $number;

セキュリティ用途かどうか判断に迷う場合は、random_int() を使うのがおすすめです。

ランダムな文字列を生成する方法

英数字のランダム文字列を生成する

英数字を組み合わせたランダム文字列を作りたい場合は、使用する文字を配列または文字列として用意し、random_int() で1文字ずつ選びます。

<?php

function generateRandomString(int $length = 16): string
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $result = '';

    for ($i = 0; $i < $length; $i++) {
        $result .= $characters[random_int(0, $charactersLength - 1)];
    }

    return $result;
}

echo generateRandomString(20);

出力例は次の通りです。

a8Kp92LmZxQ4vTnB0rYs

このコードでは、random_int() を使って文字を選んでいるため、比較的安全なランダム文字列を生成できます。

URL用のトークンを生成する

URLに含めるトークンであれば、bin2hex(random_bytes()) を使う方がシンプルです。

<?php

$token = bin2hex(random_bytes(16));

echo $token;

出力例は次の通りです。

7b9e2a4f1c0d83a6e5f921b4c8a0d2f3

random_bytes(16)bin2hex() で変換すると、32文字の16進数文字列になります。

より長いトークンが必要な場合は、random_bytes() の引数を増やします。

<?php

$token = bin2hex(random_bytes(32)); // 64文字の文字列

パスワードリセット、メール認証、CSRFトークンなどには、このような方法がよく使われます。

配列からランダムに値を取得する方法

array_rand()を使う方法

配列からランダムに要素を1つ選びたい場合は、array_rand() を使えます。

<?php

$colors = ['red', 'blue', 'green', 'yellow'];

$key = array_rand($colors);

echo $colors[$key];

array_rand() は、配列からランダムにキーを取得する関数です。

そのため、取得したキーを使って配列の値にアクセスします。

ただし、array_rand() はセキュリティ用途には向いていません。

認証、抽選、特典付与など、公平性や予測困難性が重要な場合は、random_int() を使う方がよいでしょう。

random_int()を使って配列から選ぶ方法

<?php

$colors = ['red', 'blue', 'green', 'yellow'];

$index = random_int(0, count($colors) - 1);

echo $colors[$index];

この方法であれば、random_int() によって安全にインデックスを選択できます。

配列をランダムに並び替える方法

shuffle()を使う方法

配列の順番をランダムに並び替えたい場合は、shuffle() を使います。

<?php

$items = ['A', 'B', 'C', 'D', 'E'];

shuffle($items);

print_r($items);

出力例は次の通りです。

Array
(
    [0] => C
    [1] => A
    [2] => E
    [3] => B
    [4] => D
)

shuffle() を使うと、配列の順番をランダムに変更できます。

ただし、shuffle() は元のキーを保持しません。

連想配列でキーを維持したい場合は注意が必要です。

shuffle()を使う際の注意点

shuffle() は、表示順のランダム化や簡易的な並び替えには便利です。

たとえば、以下のような用途で使えます。

<?php

$posts = ['記事A', '記事B', '記事C', '記事D'];

shuffle($posts);

foreach ($posts as $post) {
    echo $post . PHP_EOL;
}

一方で、セキュリティや公平性が重要な処理には向いていません。

重要な抽選や認証に関わる処理では、random_int() や PHP 8.2以降の Random\Randomizer を検討しましょう。

PHP 8.2以降で使えるRandom\Randomizer

Random\Randomizerとは

PHP 8.2以降では、Random\Randomizer という新しいランダムAPIが使えます。

Random\Randomizer は、ランダムな整数の生成、配列のシャッフル、配列からのキー取得などをオブジェクトとして扱える仕組みです。

エンジンを指定せずに作成した場合、デフォルトでは Random\Engine\Secure が使われます。

そのため、セキュリティを意識したランダム処理にも利用できます。

ランダムな整数を生成する

<?php

$randomizer = new Random\Randomizer();

$number = $randomizer->getInt(1, 100);

echo $number;

このコードでは、1〜100の範囲でランダムな整数を生成します。

意図を明確にしてRandom\Engine\Secureを指定する

デフォルトでも安全なエンジンが使われますが、セキュリティ用途であることを明確にしたい場合は、次のように Random\Engine\Secure を明示できます。

<?php

$randomizer = new Random\Randomizer(
    new Random\Engine\Secure()
);

$number = $randomizer->getInt(1, 100);

echo $number;

どちらの書き方でも問題ありませんが、チーム開発では意図が伝わりやすいコードを選ぶとよいでしょう。

配列からランダムにキーを取得する

<?php

$randomizer = new Random\Randomizer();

$items = ['apple', 'banana', 'orange', 'grape'];

$keys = $randomizer->pickArrayKeys($items, 1);

echo $items[$keys[0]];

pickArrayKeys() を使うと、配列からランダムにキーを取得できます。

配列をシャッフルする

<?php

$randomizer = new Random\Randomizer();

$items = ['A', 'B', 'C', 'D', 'E'];

$shuffled = $randomizer->shuffleArray($items);

print_r($shuffled);

shuffle() とは異なり、Random\Randomizer を使うと、ランダム処理をオブジェクトとして管理できます。

PHP 8.2以降の環境であれば、積極的に検討したい方法です。

ランダムな小数を生成する方法

mt_rand()を使って小数を生成する

PHPには、古いバージョンでも使える random_float() のような標準関数はありません。

簡易的に0以上1以下に近い小数を生成したい場合は、次のように書けます。

<?php

$float = mt_rand() / mt_getrandmax();

echo $float;

この方法は、簡単なテストや表示用途であれば使えます。

ただし、セキュリティ用途には向いていません。

指定した範囲の小数を生成する

たとえば、1.5〜5.5の範囲でランダムな小数を生成したい場合は、次のように書けます。

<?php

$min = 1.5;
$max = 5.5;

$randomFloat = $min + (mt_rand() / mt_getrandmax()) * ($max - $min);

echo $randomFloat;

この方法では、$min から $max の範囲に収まる小数を生成できます。

ただし、金額計算や厳密な確率処理では、浮動小数点数の丸め誤差にも注意が必要です。

PHP 8.2以降で小数を生成する

PHP 8.2以降であれば、Random\Randomizer を使ってランダムな小数を生成できます。

<?php

$randomizer = new Random\Randomizer();

$float = $randomizer->nextFloat();

echo $float;

nextFloat() は、0以上1未満のランダムな小数を返します。

環境によっては、指定範囲の小数を生成する getFloat() も利用できます。

<?php

$randomizer = new Random\Randomizer();

$float = $randomizer->getFloat(1.5, 5.5);

echo $float;

使用しているPHPのバージョンによって利用できるメソッドが異なる場合があるため、実装前に動作環境を確認しておくと安心です。

重複しないランダム値を生成する方法

range()shuffle()を使う方法

1〜10の中から重複なしで3つの数値を選びたい場合は、次のように書けます。

<?php

$numbers = range(1, 10);

shuffle($numbers);

$selected = array_slice($numbers, 0, 3);

print_r($selected);

出力例は次の通りです。

Array
(
    [0] => 7
    [1] => 2
    [2] => 9
)

この方法では、まず range(1, 10) で1〜10の配列を作ります。

その後、shuffle() で配列をランダムに並び替え、array_slice() で先頭から3つ取り出しています。

重複なしの抽選で注意すること

重複しないランダム値を作る場合は、単純に random_int() を何度も実行すると、同じ値が出る可能性があります。

たとえば、次のようなコードでは重複が発生する可能性があります。

<?php

$results = [];

for ($i = 0; $i < 3; $i++) {
    $results[] = random_int(1, 10);
}

print_r($results);

重複を避けたい場合は、あらかじめ候補の配列を作り、シャッフルしてから必要な数だけ取り出す方法がシンプルです。

ただし、景品抽選や当選者決定など、公平性が重要な処理では、shuffle() ではなく、より適切なランダム処理やログ管理、重複チェック、再抽選ルールなども検討する必要があります。

実務でよく使うランダム値の生成例

サイコロを振る

<?php

$dice = random_int(1, 6);

echo "サイコロの目は {$dice} です。";

1〜6の整数をランダムに生成することで、サイコロのような処理を実装できます。

おみくじを作る

<?php

$fortunes = ['大吉', '中吉', '小吉', '吉', '末吉', '凶'];

$index = random_int(0, count($fortunes) - 1);

echo $fortunes[$index];

配列のインデックスを random_int() で選ぶことで、おみくじのような処理を作れます。

ランダムな背景画像を表示する

<?php

$images = [
    'bg01.jpg',
    'bg02.jpg',
    'bg03.jpg',
];

$image = $images[random_int(0, count($images) - 1)];

echo '<img src="/images/' . htmlspecialchars($image, ENT_QUOTES, 'UTF-8') . '" alt="">';

Webページに出力する値は、基本的に htmlspecialchars() でエスケープしておくと安全です。

この例では画像ファイル名が固定配列から選ばれているためリスクは低いですが、ユーザー入力が関係する場合は必ずエスケープ処理を行いましょう。

ランダムなファイル名を生成する

アップロード画像などのファイル名をランダムにしたい場合は、次のように書けます。

<?php

$filename = bin2hex(random_bytes(16)) . '.jpg';

echo $filename;

出力例は次の通りです。

2a8f0b7c4d9e1f6a3c5b8d0e9f1a2b3c.jpg

ランダムなファイル名を使うことで、ファイル名の衝突や元ファイル名からの情報漏えいを防ぎやすくなります。

ただし、アップロード処理では、拡張子をユーザー入力のまま信じてはいけません。

MIMEタイプの確認、許可する拡張子の制限、保存先ディレクトリの制御なども必要です。

CSRFトークンを生成する

CSRF対策用のトークンは、random_bytes() を使って生成できます。

<?php

session_start();

$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

echo $_SESSION['csrf_token'];

フォームに埋め込む場合は、次のようにします。

<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">

CSRFトークンは、ユーザーごと・セッションごとに管理し、フォーム送信時にサーバー側で照合します。

Laravelでランダム文字列を生成する方法

Str::random()を使う方法

Laravelでは、ランダム文字列を生成するために Str::random() がよく使われます。

<?php

use Illuminate\Support\Str;

$token = Str::random(40);

echo $token;

Laravelアプリケーションでは、フレームワーク側で便利なヘルパーが用意されているため、素のPHPとは少し書き方が異なる場合があります。

ただし、PHPの基本としては、random_int()random_bytes() を理解しておくことが重要です。

セキュリティ用途で使うべきランダム生成方法

認証コードにはrandom_int()を使う

メール認証やSMS認証などで使う数値コードには、random_int() を使います。

<?php

$code = random_int(100000, 999999);

4桁コードは総当たりされやすいため、実務では6桁以上を検討することが多いです。

あわせて、有効期限、試行回数制限、IP制限、レート制限なども必要です。

トークンにはrandom_bytes()を使う

パスワードリセット、メール認証、CSRF対策、APIキーなどには、random_bytes() を使います。

<?php

$token = bin2hex(random_bytes(32));

random_bytes(32) を使うと、十分に長いランダムデータを生成できます。

bin2hex() で変換すれば、URLやデータベースにも保存しやすい文字列になります。

景品抽選やクーポン発行にも注意する

景品抽選、クーポン発行、ポイント付与などは、一見セキュリティとは関係なさそうに見えます。

しかし、金銭的な価値や公平性が関係する場合は、推測や操作を避ける必要があります。

そのため、単純な mt_rand() ではなく、random_int()Random\Randomizer を使う方が安全です。

セキュリティ用途で避けるべき書き方

mt_rand()でトークンを作る

次のような書き方は避けましょう。

<?php

$token = md5(mt_rand());

mt_rand() は暗号学的に安全な乱数ではありません。

また、md5() に通しても、元の乱数が弱ければ安全なトークンにはなりません。

正しくは、次のように書きます。

<?php

$token = bin2hex(random_bytes(32));

認証コードに短すぎる値を使う

次のように、4桁のコードを使う場合は注意が必要です。

<?php

$code = random_int(1000, 9999);

4桁コードは、0000〜9999までの組み合わせが少ないため、総当たり攻撃に弱くなります。

より安全にするなら、6桁以上のコードを使い、有効期限や試行回数制限も組み合わせましょう。

<?php

$code = random_int(100000, 999999);

アップロードファイル名に元の名前をそのまま使う

アップロードされたファイル名をそのまま保存するのも避けた方がよいです。

<?php

$filename = $_FILES['image']['name'];

このような書き方では、ファイル名の衝突や情報漏えいにつながる可能性があります。

ファイル名をランダム化するなら、次のように書けます。

<?php

$filename = bin2hex(random_bytes(16)) . '.jpg';

ただし、実際のアップロード処理では、拡張子やMIMEタイプの検証も必ず行いましょう。

PHPでランダム値を生成するときの使い分け

セキュリティに関係する場合

次のような用途では、random_int() または random_bytes() を使います。

用途推奨される方法
6桁の認証コードrandom_int()
パスワードリセットトークンrandom_bytes()
メール認証トークンrandom_bytes()
CSRFトークンrandom_bytes()
APIキーrandom_bytes()
招待コードrandom_int() または random_bytes()
景品抽選random_int() または Random\Randomizer

数値が必要な場合は random_int()、トークンのような長い文字列が必要な場合は random_bytes() を使うと考えるとわかりやすいです。

セキュリティに関係しない場合

次のような用途であれば、mt_rand()shuffle() も使えます。

用途使用できる方法
ランダムなメッセージ表示mt_rand() / random_int()
テストデータ生成mt_rand()
表示順のランダム化shuffle()
簡単なおみくじmt_rand() / random_int()
簡単なゲーム処理mt_rand() / random_int()

ただし、最近のPHPでは random_int() も簡単に使えるため、迷った場合は random_int() を使っておくとよいでしょう。

PHPでランダム値を生成するときのまとめ

PHPでランダムな値を生成する場合は、用途に応じて関数を使い分けることが重要です。

セキュリティに関係するランダム整数には、random_int() を使います。

<?php

$number = random_int(1, 100);

トークンやAPIキーのようなランダム文字列には、random_bytes() を使います。

<?php

$token = bin2hex(random_bytes(32));

PHP 8.2以降であれば、Random\Randomizer も利用できます。

<?php

$randomizer = new Random\Randomizer();

$number = $randomizer->getInt(1, 100);

一方で、mt_rand()rand() は暗号学的に安全ではありません。

表示内容のランダム切り替えやテストデータ生成など、セキュリティに関係しない簡易用途に限定して使うのが安全です。

特に、次のような用途では mt_rand()rand() を使わないようにしましょう。

  • 認証コード
  • パスワードリセットトークン
  • メール認証トークン
  • CSRFトークン
  • APIキー
  • 景品抽選
  • クーポン発行
  • 金銭やポイントに関わる処理

PHPでランダム値を生成するときは、推測されると困る値には random_int() または random_bytes() を使うという考え方を基本にすると、より安全な実装ができます。

以上、PHPでランダムな値を生成する方法についてでした。

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

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