より高速に、推測困難な一意なIDを生成する方法

PHP7以降では等確率に選ばれるランダムな値を得る関数に、random_int() と random_bytes() があります。
random_int()は数字、random_bytes()はバイト文字列です。
これを使用すると一意なキーの生成は次のようになります。

sha1(random_bytes(10));

PHP5以前や下位互換を考える時には、一意なキーを生成するのに、uniqid()があります。

これは現在時間をマイクロ秒単位にしたものを使用しています。
つまりランダムというより、重複のない値となります。
PHPのuniqid()のマニュアルページにも、「戻り値の一意性を保証するものではありません。」とあります。

戻り値は、『 4a4f513eb71b1 』のような半角英数字の13文字です。16進数文字なので、「0から9 aからf」です。

第一引数にはプレフィックス、第二引数には追加のエントロピーの使用の有無を渡すことができます。

第二引数はデフォルトは false になっていますが、trueにするとドット『 . 』と数字9文字が追加され、文字数が23文字になります。
『 4a4f5caadd40c2.19903592 』このような値です。

半角英数字のみにするにはsha1ハッシュ変換します。

sha1(uniqid(null, true))

sha1の戻り値は、40文字の半角英数字(0から9 aからf)です。

セッションクッキーなどの推測されては困るようなトークンに使用する場合には、いくつかの関数を組み合わせます。

例えば、プレフィックスにも uniqid() をつける方法があります。

sha1(uniqid(uniqid(), true))

これよりほかの乱数エントロピーを組み合わせたほうがより推測困難になります。

sha1(uniqid(dechex(rand()), true))

より高速にするためには、rand()よりもmt_rand()を使用します。

sha1(uniqid(dechex(mt_rand()), true))

これで推測困難なIDを高速に生成することができます。

PHP7.4以降ではuniqid()の第一引数にmt_rand()などをそのまま指定すると次のようなエラーが出ます。

uniqid() expects parameter 1 to be string, int given

第一引数はstring型が宣言されているのでmt_rand()だとint型となりエラーが出ます。
このため10進数を16進数に変換するdechex()を使用しています。

サンプルコード

echo sha1(uniqid(dechex(mt_rand()), true));

とすると

46e1e7d630f1ff4d2ba8d9c295758df5

と返ります。

注意点

md5は衝突性(コリジョン)があることが証明されていて、セキュリティホールとなることが潜在的に存在します。
このためsha1を使用するほうがいいです。

md5は半角英数文字で構成されるため0から9、aからzの10+26文字=36文字と思っている人がいますが、これは間違いです。
md5は16進数32桁なので0から9、aからfの10+6文字=16文字
「16の32乗」通りです。
(2進数にすると128ビットになります)

同様にsha1は16進数40桁なので「16の40乗」通りです。
(2進数にすると160ビットになります)

第2引数にtrue を指定すると、sha1 ダイジェストは 20 バイト長のバイナリ形式の値を返します。
これはバイナリなので画面に表示させると文字化けのようになります。

関連記事

スポンサーリンク

DOCUMENT ROOTを得る $_SERVER["DOCUMENT_ROOT"]は使えない!

ホームページ製作・web系アプリ系の製作案件募集中です。

上に戻る