PHP Tips | 文字列処理:ランダム・生成 – ランダム文字列生成(英数字)

PHP PHP
スポンサーリンク

何のために「ランダム英数字文字列」を作るのか

まず、どんな場面で使うかをイメージしましょう。

ユーザーごとの招待コード
一時的なトークン(メール認証、パスワードリセット)
ファイル名やディレクトリ名の衝突回避用の識別子

こういうときに「ランダムな英数字の文字列」が欲しくなります。

ここで大事なのは、「なんとなくランダムっぽい」ではなく、
「推測されにくい」「偏りが少ない」ランダムさをきちんと確保することです。


絶対に押さえておきたいポイント:乱数は「暗号論的に安全なもの」を使う

mt_rand() や rand() を使ってはいけない場面

PHP には rand()mt_rand() がありますが、
これらは「ゲームのサイコロ」レベルの乱数であって、
セキュリティ用途(トークン、招待コードなど)には向きません。

理由はシンプルで、「内部状態が推測されると、次の値が予測されうる」からです。

なので、

認証・認可・セキュリティに関わるランダム値
URL に載るトークン
ユーザーごとに一意であることが重要な識別子

こういった用途では、必ず「暗号論的に安全な乱数」を使います。

PHP でそれに該当するのが random_bytes()random_int() です。


一番シンプルな実装:random_bytes() + 文字集合からの取り出し

文字集合を決める

まず、「どんな文字を使うか」を決めます。

よく使うのは「0–9, a–z, A–Z」の 62 文字です。

$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
PHP

この中からランダムに文字を選んで、指定した長さ分つなげていきます。

実装例:任意長のランダム英数字文字列を生成する関数

/**
 * ランダムな英数字文字列を生成する
 *
 * @param int $length 生成する文字列の長さ
 * @return string
 */
function random_alnum_string(int $length): string
{
    if ($length <= 0) {
        throw new InvalidArgumentException('length must be positive');
    }

    $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charsLength = strlen($chars);

    $result = '';

    // 必要な文字数分ループ
    for ($i = 0; $i < $length; $i++) {
        // 0〜$charsLength-1 の暗号論的に安全な乱数
        $index = random_int(0, $charsLength - 1);
        $result .= $chars[$index];
    }

    return $result;
}
PHP

使い方の例です。

echo random_alnum_string(8);   // 例: "a9F3kL0z"
echo random_alnum_string(32);  // 例: "G7k2Pq9sL0xZ1mN8cD4vB6tY3wR5hU"
PHP

ここでの重要ポイントは、
「インデックスの取得に random_int() を使っている」ことです。

random_int() は内部的に暗号論的に安全な乱数ソースを使うので、
トークンや招待コードにも安心して使えます。


もう一つのパターン:random_bytes() から英数字にマッピングする

なぜ random_bytes() を使うパターンも知っておくと良いか

random_int() ベースの実装で十分ですが、
「バイト列として乱数を取り出して、それを文字にマッピングする」
というパターンもよく使われます。

これは、例えば「まず 16 バイトのランダム値を作って、それを 32 桁の 16 進文字列にする」ようなケースです。

$bytes = random_bytes(16);
$token = bin2hex($bytes); // 32 桁の 0-9a-f
PHP

これはこれで便利ですが、「英数字(0–9, a–z, A–Z)」に限定したい場合は、
さっきの random_int() パターンの方が素直です。


実務での使いどころのイメージ

パスワードリセット用トークン

例えば、パスワードリセット用の URL に載せるトークンを作るとします。

$token = random_alnum_string(64);

// DB に保存
// password_reset_tokens (user_id, token, expires_at, ...)

// ユーザーに送る URL
$url = 'https://example.com/reset-password?token=' . $token;
PHP

ここで mt_rand() などを使ってしまうと、
「トークンが推測される」リスクが一気に上がります。

random_alnum_string() の中で random_int() を使っていれば、
その心配はかなり小さくできます。

一時ファイル名・ディレクトリ名

一時ファイルや一時ディレクトリを作るときにも、
ランダムな英数字を足しておくと衝突しにくくなります。

$baseDir = '/tmp/myapp';

$dirName = 'session_' . random_alnum_string(16);
// /tmp/myapp/session_a9F3kL0zXy12AbCd みたいな感じ

$path = $baseDir . '/' . $dirName;
mkdir($path, 0700, true);
PHP

ここでは「セキュリティ」だけでなく、「一意性」も大事です。
random_alnum_string() を使っておけば、
他のプロセスと名前がぶつかる可能性をかなり下げられます。


まとめ:今日からの「ランダム英数字文字列生成」ユーティリティ

押さえておきたいポイントは、次の 3 つです。

ランダム文字列をセキュリティ用途に使うなら、rand()mt_rand() ではなく、必ず random_int() / random_bytes() を使う。
英数字だけのランダム文字列は、「文字集合を決めて、そのインデックスを random_int() で選ぶ」実装がシンプルで安全。
トークン、招待コード、一時ファイル名など、「推測されたら困る」「一意であってほしい」場面では、このユーティリティを必ず経由するようにする。

核になるコードは、これです。

function random_alnum_string(int $length): string
{
    if ($length <= 0) {
        throw new InvalidArgumentException('length must be positive');
    }

    $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charsLength = strlen($chars);

    $result = '';

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

    return $result;
}
PHP

もし、あなたのコードの中で「mt_rand() をループして英数字を作っている」箇所があれば、そこがこの関数に置き換えるべきポイントです。
その一箇所を差し替えるだけで、アプリ全体の“ランダムの質”が一段上がります。

PHPPHP
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました