PHPのuniqid()は、現在時刻をもとに重複しにくい識別子を生成する関数です。
ファイル名や一時的なIDなど、簡単に識別用の文字列を作りたい場合に利用できます。
基本的な使い方は非常に簡単です。
$id = uniqid();
echo $id;
実行すると、次のような文字列が生成されます。
68a1543c15d24
ただし、uniqid()という名前から「必ず一意になるIDを生成できる」と考えるのは適切ではありません。uniqid()は重複しにくい値を生成するための関数ですが、一意性そのものは保証されていません。
また、暗号学的に安全なランダム文字列を生成する関数でもないため、ログイントークンやパスワードリセットトークンなどのセキュリティ用途には使用しないことが重要です。
uniqid関数の基本的な書き方
uniqid()の基本構文は次のとおりです。
uniqid(string $prefix = "", bool $more_entropy = false): string
uniqid()には、$prefixと$more_entropyという2つの引数があります。
どちらも省略できるため、もっとも簡単な使い方は次のとおりです。
$id = uniqid();
prefixとは
第1引数の$prefixには、生成するIDの先頭に付ける文字列を指定できます。
たとえば、次のように記述します。
$id = uniqid('user_');
echo $id;
生成結果は次のようなイメージです。
user_68a1543c15d24
このように接頭辞を付けることで、そのIDが何に使用されているものなのかを分かりやすくできます。
たとえば、ユーザー、注文、画像などで使い分けることができます。
$userId = uniqid('user_');
$orderId = uniqid('order_');
$imageId = uniqid('image_');
生成結果の例は次のとおりです。
user_68a1543c15d24
order_68a1543c15d50
image_68a1543c15d71
ただし、prefixを付けたからといって、生成される値のランダム性やセキュリティが高くなるわけではありません。
more_entropyとは
第2引数の$more_entropyには、追加のエントロピーを付加するかどうかを真偽値で指定します。
通常はfalseです。
$id = uniqid();
追加のエントロピーを付ける場合は、次のようにtrueを指定します。
$id = uniqid('', true);
echo $id;
生成結果は次のような形式になります。
68a1543c15d241.83947251
more_entropyをtrueにすると、通常のuniqid()よりも長い文字列が返されます。
ただし、more_entropyを有効にしても、暗号学的に安全なランダム値になるわけではありません。
uniqid関数で生成される文字数
引数を指定せずにuniqid()を実行した場合、基本的には13文字の文字列が生成されます。
$id = uniqid();
echo strlen($id);
実行結果は次のようになります。
13
一方、more_entropyをtrueにすると、空のprefixの場合は23文字の文字列になります。
$id = uniqid('', true);
echo strlen($id);
実行結果は次のようになります。
23
なお、prefixを指定した場合は、その文字数が追加されるため、全体の文字数も長くなります。
uniqid関数はどのような仕組みでIDを生成するのか
uniqid()を理解するうえで重要なのは、完全にランダムな値を生成しているわけではないという点です。
uniqid()は、現在時刻をマイクロ秒精度で利用して識別子を生成します。
そのため、大まかな考え方としては次のようになります。
現在時刻
↓
識別子として使える文字列へ変換
↓
値を返す
たとえば、短い時間間隔で何度か実行した場合、次のように似た文字列になることがあります。
68a1543c15d24
68a1543c15d29
68a1543c15d2d
これは、uniqid()が完全なランダム値ではなく、時刻を中心として値を生成しているためです。
uniqid関数は本当に重複しないのか
uniqid()を使用するときに、もっとも注意したいのが一意性です。
関数名に「unique」という言葉が含まれていますが、生成される値が絶対に重複しないことを保証する関数ではありません。
つまり、次のコードで作られたIDは重複する可能性があります。
$id = uniqid();
uniqid()は、あくまで重複しにくい識別子を簡単に作るための関数として考えるのが適切です。
特に、複数のサーバーや複数のプロセスで大量のIDを同時に生成するような環境では、uniqid()だけに一意性を依存する設計は避けたほうがよいでしょう。
単一サーバーでも絶対ではない
「1台のサーバーだけで使用していれば絶対に重複しない」と考えることもできません。
システムクロックの調整など、環境によっては同じような時刻情報が利用される可能性があります。
そのため、重複すると重大な問題につながるIDでは、データベースのユニーク制約やUUIDなどを利用して一意性を管理することが重要です。
uniqid関数の主な使い方
uniqid()は、セキュリティ上重要ではなく、厳密な一意性を求めない簡易的な識別子を作る用途で利用できます。
一時的なIDを作る
アプリケーション内部で一時的にデータを区別したい場合などに利用できます。
$tempId = uniqid('tmp_');
echo $tempId;
実行結果の例です。
tmp_68a1543c15d24
一時的な処理の識別など、重複した場合でも再生成できる用途であれば利用しやすい方法です。
ファイル名の一部として使う
ファイル名の重複を避けやすくする目的でも使用できます。
$fileName = uniqid('image_') . '.jpg';
echo $fileName;
生成結果の例です。
image_68a1543c15d24.jpg
ただし、ファイル名が絶対に重複してはいけない場合は、uniqid()だけに依存するべきではありません。
また、ユーザーからアップロードされたファイルを扱う場合は、ファイル名だけでなく、MIMEタイプや拡張子、保存場所、実行権限などの安全対策も必要です。
注文番号などの表示用識別子に使う
次のように、表示用の簡易的な番号を作ることもできます。
$orderNumber = 'ORDER-' . uniqid();
echo $orderNumber;
生成結果は次のようなイメージです。
ORDER-68a1543c15d24
ただし、本番のECサイトなどで注文IDの一意性が重要な場合、uniqid()だけで正式な注文IDを管理するのは適切ではありません。
データベース側で主キーやユニーク制約を設定するなど、別の仕組みで一意性を保証する必要があります。
uniqid関数はセキュリティ用途には向いていない
uniqid()を使用するときに特に重要なのが、セキュリティ用途には利用しないことです。
uniqid()は現在時刻をベースとして生成されるため、暗号学的に安全な乱数ではありません。
たとえば、次のような使い方は避ける必要があります。
$passwordResetToken = uniqid();
パスワードリセット用トークンには、第三者から推測されにくい値が必要です。
しかし、uniqid()はそのような目的のために設計された関数ではありません。
uniqidを使わないほうがよい用途
次のような値を生成するときは、uniqid()を使用しないほうがよいでしょう。
- パスワードリセットトークン
- メール認証用トークン
- ログイントークン
- APIキー
- CSRFトークン
- セッション用の秘密値
- 暗号処理に利用する秘密情報
これらには、暗号学的に安全な乱数生成機能を利用する必要があります。
セキュリティ用途ではrandom_bytes関数を使う
PHPで暗号学的に安全なランダムデータを生成したい場合は、random_bytes()を利用できます。
たとえば、次のように記述します。
$token = bin2hex(random_bytes(16));
echo $token;
生成結果は次のようなイメージです。
8d2b51b78ea452a105f6f1c54fe3492d
random_bytes(16)では16バイトのランダムデータが生成されます。
ただし、生成されたデータにはそのまま表示しにくいバイトが含まれる可能性があります。
そのため、Webアプリケーションで扱いやすい文字列にしたい場合は、bin2hex()などでエンコードする方法がよく使われます。
random_bytesでより長いトークンを作る
たとえば、32バイトのランダムデータを生成する場合は次のようにします。
$token = bin2hex(random_bytes(32));
32バイトを16進数に変換すると、64文字の文字列になります。
セキュリティトークンなどでは、uniqid()ではなく、このような暗号学的に安全な乱数生成方法を使用するのが基本です。
uniqidとrandom_bytesの違い
uniqid()とrandom_bytes()は、目的が大きく異なります。
uniqid()は重複しにくい識別子を簡単に生成する関数です。
一方、random_bytes()は暗号学的に安全なランダムデータを生成するための関数です。
主な違いを整理すると、次のようになります。
| 項目 | uniqid() | random_bytes() |
|---|---|---|
| 主な目的 | 簡易的な識別子の生成 | 安全なランダムデータの生成 |
| 基本的な仕組み | 時刻ベース | CSPRNG |
| 一意性の保証 | ない | 一意性を直接保証する関数ではない |
| 予測困難性 | 低い | 高い |
| セキュリティ用途 | 不向き | 適している |
| 一時的な識別子 | 利用できる | 利用できる |
| CSRFトークン | 不向き | 適している |
| パスワードリセットトークン | 不向き | 適している |
重要なのは、random_bytes()も「一意性を保証するための関数」ではないという点です。
ただし、十分な長さの暗号学的ランダム値を利用すれば、実用上の衝突確率を極めて小さくできます。
uniqidとmd5を組み合わせれば安全になるのか
古いPHPのコードでは、次のような記述を見ることがあります。
md5(uniqid());
また、次のようなコードもあります。
md5(uniqid(mt_rand(), true));
しかし、uniqid()の結果をmd5()でハッシュ化したからといって、暗号学的に安全な乱数になるわけではありません。
ハッシュ関数は入力されたデータを別の形式に変換するものであり、元の値に存在しなかった十分なランダム性を新たに生み出すものではないからです。
安全なトークンを生成するのであれば、最初から次のような方法を利用するほうが適切です。
$token = bin2hex(random_bytes(32));
uniqidとUUIDの違い
uniqid()とUUIDは、どちらも識別子として使われることがありますが、同じものではありません。
uniqid()はPHPが提供する時刻ベースの識別子生成関数です。
一方、UUIDは標準化された128ビットの識別子です。
UUIDは、次のような形式で表現されます。
550e8400-e29b-41d4-a716-446655440000
UUIDには複数のバージョンや生成方法があり、用途に応じて適切な方式を選択します。
複数サーバーでIDを生成するシステムや、大規模な分散システムでは、単純なuniqid()よりUUIDなどが適している場合があります。
ただし、UUIDについても「絶対に重複しない」と表現するのではなく、方式や用途に応じて衝突可能性を考慮する必要があります。
uniqidをデータベースの主キーに使ってもよいのか
uniqid()で生成した文字列をデータベースの主キーとして使用すること自体は技術的には可能です。
たとえば、次のようにIDを生成できます。
$id = uniqid();
しかし、データベースの主キーとしてuniqid()だけに一意性を依存する設計は慎重に考える必要があります。
特に、次のような要件がある場合には別の方法を検討したほうがよいでしょう。
- IDが絶対に重複してはいけない
- 複数サーバーから同時にIDを生成する
- 大量のIDを高速に生成する
- IDを外部ユーザーに公開する
- IDから生成時刻などを推測されたくない
このような場合には、AUTO_INCREMENT、データベースシーケンス、UUID、ULIDなど、システム要件に適した方法を検討します。
また、アプリケーション側のID生成方式にかかわらず、重複が許されないデータにはデータベース側でPRIMARY KEYやUNIQUE制約を設定することが重要です。
uniqid関数を使うときの注意点
uniqid()を利用するときは、特に一意性とセキュリティの違いを理解しておくことが重要です。
一意性は保証されない
uniqid()という名前でも、必ず異なる値が生成されるわけではありません。
$id = uniqid();
によって作られる値は、重複しにくい識別子ではありますが、重複しないことを保証する値ではありません。
重複すると重大な問題が発生する場合は、データベースの一意制約などを組み合わせる必要があります。
more_entropyを過信しない
次のようにmore_entropyを有効にすると、追加のエントロピーが付加されます。
$id = uniqid('', true);
しかし、これによってセキュリティトークンとして安全になるわけではありません。
more_entropyは、uniqid()を暗号学的に安全な乱数生成関数へ変えるオプションではありません。
時刻情報をベースにしていることを理解する
uniqid()は現在時刻を利用して生成されます。
そのため、完全にランダムで予測不能な値ではありません。
生成値を第三者に見せる場合や、推測されることで問題が生じる用途には適していません。
uniqid関数が向いている用途
uniqid()は、厳密な一意性やセキュリティを求めない用途であれば利用できます。
たとえば、次のような用途が考えられます。
- 一時的な内部識別子
- 一時ファイル名の一部
- ログ上の簡易識別子
- テストデータ用の識別子
- 重複した場合に再生成できる名称
- セキュリティを必要としない簡易ID
これらの用途でも、重複が絶対に許されない場合には別の仕組みを利用する必要があります。
uniqid関数が向いていない用途
一方、次のような用途ではuniqid()を使用しないほうがよいでしょう。
- パスワードリセットトークン
- メール認証用トークン
- ログイントークン
- APIキー
- CSRFトークン
- 秘密情報の生成
- 暗号鍵
- 絶対に重複してはいけないID
セキュリティ用途ではrandom_bytes()などを利用し、一意性が重要なデータではデータベースの主キーやユニーク制約、UUIDなどを利用します。
PHPのuniqid関数についてのまとめ
PHPのuniqid()は、現在時刻をマイクロ秒精度で利用して、重複しにくい識別子を生成する関数です。
もっとも基本的な使い方は次のとおりです。
$id = uniqid();
接頭辞を付けることもできます。
$id = uniqid('user_');
さらに、第2引数をtrueにすることで追加のエントロピーを付加できます。
$id = uniqid('user_', true);
ただし、uniqid()については、次の2点を特に理解しておくことが重要です。
一意性は保証されないこと。
暗号学的に安全なランダム値ではないこと。
そのため、用途ごとに使い分ける必要があります。
簡易的な識別子
→ uniqid()
安全なランダムトークン
→ random_bytes()
厳密に一意性を管理したいID
→ データベースの主キー・UNIQUE制約・UUID・ULIDなど
uniqid()は手軽に利用できる便利な関数ですが、関数名だけを見て「絶対に重複しないIDを作れる」と判断するのは適切ではありません。
PHPでIDを生成するときは、一意性、ランダム性、予測困難性のうち、どの性質が必要なのかを明確にしてから適切な方法を選択することが重要です。
以上、PHPのuniqid関数についてでした。
最後までお読みいただき、ありがとうございました。










