PHPでuniqid()を安全に使うために試行錯誤してみる

by

in

PHPでユニークなIDを生成するためにそれっぽい名前の uniqid() がありますが、こちらは戻り値の一意性を保証されないものになっています。

警告

この関数は、戻り値の一意性を保証するものではありません。

https://www.php.net/manual/ja/function.uniqid.php

つまり、ユニークなIDを取得するには相応しくない関数ということになります。

この uniqid() 関数の使い方や使い道、ユニークなIDを取得する方法などをまとめてみたいと思います。

uniqid() の使い方

<?php

// 通常の使用
uniqid(); // string(13) "64f1fc67a9236"

// プレフィックスを指定する場合
$prefix = 'myPrefix_';
uniqid($prefix); // string(22) "myPrefix_64f1fc67a924f"

// マイクロ秒単位のタイムスタンプを含める場合
uniqid($prefix, true); // string(32) "myPrefix_64f1fc67a92539.84035833"

uniqid() は実行時のマイクロ秒で 13文字の文字列を返します。

マイクロ秒を元に生成するため、実行時のタイミングでは同じ文字列を出力されてしまいます。

第1引数の $prefix は文字列の前半に指定した値を結合します。この指定が有効なパターンとしては複数ホストで同時にIDを作成するシステム構造に使用する事が想定されます。

第2引数には $more_entropy として true を渡すことで新たな乱数を結合して結果が一意になる可能性を高めます。こちらの引数が指定された場合には 23文字になります。

つまり、複数ホストでの実行があれば $prefix でホスト名を記載し、第2引数の $more_entropytrue にする事でマイクロ秒以外の乱数を結合することでユニークな可能性が高いIDを取得することができます。

PHP8系では複数回実行しても前回の実行を記録しており、なるべく重複しない値を取得する実装になっているようです。

参考: https://medium.com/paronym/phpのuniqid-を連続で呼んでも重複しないのは何故か-e9adf48f8ab0

uniqid() はPHP4系から存在する関数

uniqid() でも引数を適切に指定することで、ユニークな可能性が高いIDが取得出来る事を解説してきました。

ですが、この uniqid() はPHP4系から存在している関数になります。PHP4系は2000年5月にリリースされたものなので現在 (2023年9月) で23年前の実装です。

かなり昔に実装された物であるということと、下位互換のためにそれほど出力結果は変わらないはずなので、そのまま関数が残っているのではと感じています。

現在、一般的に使われている暗号手法でも量子コンピューターのように計算速度が飛躍的に向上してしまうと脆弱になってしまうため、23年の期間もあれば技術が陳腐化してしまいます。

新たなユニークIDを生成する手法なども確立しており、UUIDなどの他の方法でユニークIDを生成する方が素早く確実な物を取得することが可能になっています。

古いから使わない方がいいとは思いませんが、少なくとも暗号化のためのユニークIDを生成するのには使わない方が良いです。

  • 暗号学的なセキュアな値は必要ない
  • 古くからある枯れた技術だからこその安全性

を重視したユニークIDを取得したい場合に、uniqid() は選択肢に上がる方法かと考えます。

割と用途が限定的な物になってしまうので、フレームワークなどでUUIDの生成ができるのであればUUIDを使用したり、別の手法を使用してもいいかもしれません。

暗号学的なセキュアなユニークIDを取得する方法

uniqid() が苦手とする暗号学的にセキュアな方法でランダムな文字列や数値を取得する方法を紹介します。

<?php

$bytes = random_bytes(32);
bin2hex($bytes); // string(64) "b087a977c9b91d1b1d44b4d5d0fce9596a6cfa877ea2ae9f9098ab9967a12a17"

random_int(100, 999); // int(311)
random_int(-1000, 0); // int(-862)

こちらの random_bytes()random_int() を使用することで暗号学的にセキュアな値を取得することが可能になります。

他にも、UUIDを使用する方法があり、こちらの記事にもまとめました。

終わりに

以上、PHPの uniqid() についてまとめてみました。

ユニークIDを使いたい用途を正しく整理し、暗号学的にセキュアな値が必要な場面(セッションIDなどの、外部から参照されて変更が可能な値等)には uniqid() は不向きな物でした。

永続的ではなく、一時的なユニークIDとして使用するのであれば uniqid() はアリかと感じています。が、引数の指定は適切に設定しましょう。

お疲れ様でした。

投稿者


Comments

コメントを残す

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


モバイルバージョンを終了