web-dev-qa-db-ja.com

PHP内でランダムキーを生成する最良の方法は何ですか?

選択した長さ(2〜1000以上)の印刷可能なACSII文字を含むランダムキーを生成する再利用可能な関数を作成しようとしています。印刷可能ASCII文字は33-126になるでしょう。キーは完全に一意である必要はなく、まったく同じミリ秒で生成される場合は一意である必要があります(したがってuniqid()動作しません)。

私はchr()mt_Rand()の組み合わせがうまくいくと思っています。

これは進むべき道ですか、それとも他の最善の方法ですか?

編集:uniqid()も長さパラメーターがないため機能しません。PHPが与えるものは何でも)です。

私のアイデア:これは私が思いついたものです:

function GenerateKey($length = 16) {
    $key = '';

    for($i = 0; $i < $length; $i ++) {
        $key .= chr(mt_Rand(33, 126));
    }

    return $key;
}

これに何か問題はありますか?

別の編集:他の質問のほとんどは、パスワードの生成に関するものです。さまざまなキャラクターが欲しいのですが、1 vs l。可能なキーの最大数を可能にしたい。

注:生成されたキーは必ずしも暗号的に安全である必要はありません。

39
Darryl Hein

更新(2015年12月):PHP 7.0では、提供するmt_Randの代わりに random_int() を使用する必要があります「暗号的に安全な値」

個人的には、sha1(microtime(true).mt_Rand(10000,90000))を使用したいのですが、よりカスタマイズ可能なアプローチを探しているので、この関数を試してください(これは this answer のリクエストに対する変更です):

function Rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_Rand(33, 126));
  }
  return $random;
}

それでも、これはおそらくuniqid()、md5()、またはsha1()よりも大幅に遅くなります。

編集:最初に到達したようですが、ごめんなさい。 :D

編集2:私は、DebianマシンでPHP 5とeAcceleratorを使用して、すてきな小さなテストを行うことにしました(長いコードを言い訳に):

function Rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_Rand(33, 126));
  }
  return $random;
}

function Rand_sha1($length) {
  $max = ceil($length / 40);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= sha1(microtime(true).mt_Rand(10000,90000));
  }
  return substr($random, 0, $length);
}

function Rand_md5($length) {
  $max = ceil($length / 32);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= md5(microtime(true).mt_Rand(10000,90000));
  }
  return substr($random, 0, $length);
}

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_char(1000);

echo "Rand:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_sha1(1000);

echo "SHA-1:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_md5(1000);

echo "MD5:\t".(microtime(true) - $a)."\n";

結果:

Rand:   2.09621596336
SHA-1:  0.611464977264
MD5:    0.618473052979

ですから、速度(ただし完全な文字セットではない)が必要な場合は、MD5、SHA-1、またはUniqidに固執することをお勧めします(まだテストしていません。)

57

暗号強度のランダム性が必要な場合、ここでの答えはどれも十分ではありません(ランダムキーが何であるかを推測しようとしている攻撃者がいますか?)。時間のハッシュは安全ではありません。攻撃者は、サーバーがキーを生成したと考える時間を推測することで検索を大幅に高速化できます。また、商品のラップトップでも、ビットサーチスペース)。また、ハッシュ関数を介してuniqid()またはその他の弱いランダムソースの結果を実行して「展開」するという提案は危険です。これをやった。

本当に暗号レベルのセキュリティが必要な場合は、/ dev/randomから読み取る必要があります。POSIX準拠のシステム(Windows以外)で次のコードが機能するはずです。

#Generate a random key from /dev/random
function get_key($bit_length = 128){
    $fp = @fopen('/dev/random','rb');
    if ($fp !== FALSE) {
        $key = substr(base64_encode(@fread($fp,($bit_length + 7) / 8)), 0, (($bit_length + 5) / 6)  - 2);
        @fclose($fp);
        return $key;
    }
    return null;
}

もう少し速度が必要な場合は、代わりに「dev/urandom」から読み取ることができます。

28
SecurityJoe

引き続きuniqid()を使用できますが、必要な文字数に値を拡張するための追加処理を行うだけです。

たとえば、32文字に拡張するには、次のようにします。

_$id = md5(uniqid());
_

64文字に拡張するには、md5のmd5を次のように追加します

_$first = md5(uniqid());
$id = $first . md5($first);
_

次に、32の倍数未満が必要な場合は、必要に応じて構造化します。

衝突が発生する可能性はありますが、ほとんどありません。あなたがそれについて妄想している場合は、同じアイデアを使用しますが、ハッシュする代わりにAESのような対称暗号を介してuniqid()を一気飲みします。

8
Bob Somers

Openssl_random_pseudo_bytesを使用しない理由 http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

3
omar-ali

私の以前の答えは、システム変数から派生したソルトと組み合わせたuniqid()のハッシュを取りましたが、randomキーを生成せず、一意である可能性が高いキーのみ。同様に、Rand()またはmt_Rand()からの出力は、暗号化の目的には十分にランダムではありません。

ランダムキーを生成するには、適切なランダムデータソースに移動する必要があり、関数 openssl_random_pseudo_bytes() が設計されていますそれを行うには:

$randkey = base64_encode(openssl_random_pseudo_bytes(32));

Base64_encode()は、印刷可能な文字にすることです。

他の回答がopenssl_random_pseudo_bytes()を推奨しない理由は、おそらくPHP 5.3で導入されたため、この質問はほぼ6年前のものです。

1
thomasrutter

その質問 はあなたにとって興味深いものですか?

なぜuniqid()がうまくいかないのか、どの場合でも同じミリ秒の一意の数字が必要ですが、そうでない場合は必ずしもそうではありません。同じミリ秒で衝突する可能性があるほど速く生成しているものは何ですか? uniqid()がその数を生成するのにどれだけ時間がかかるのかと思います。必要に応じて、uniqid()関数のprefixパラメーターをいくつかのランダムな文字とともに使用すると、安全になります。

ファイルを生成する場合は、 tmpfile() または tempname() をご覧ください。

いずれにせよ、達成しようとしていることに応じて、一意のIDが既に(配列で、file_existsなどで)取得されているかどうかをループして確認し、その場合は別のIDを生成できます。


また、あなたの質問を正確に理解しているかどうかはわかりませんが、あなたの質問の違いがわかりますが、かなり似ていると思われる他の質問を指摘します。

最初の1つは、人間が読める一意のIDを探している場合に役立ちます。 2番目の方法は、乱数とmd5/sha1で遊ぶ場合に便利です。繰り返しますが、uniqid()は既に探しているものであると思います。

0
lpfavreau

$ key = md5(microtime()。Rand());

マイクロタイム自体は、推測される可能性が1/1000しか残っていないため、安全ではありません。

ランド自体もあまり安全ではありませんが、それらを連結してハッシュ化すると、かなり強固なランダム化が得られます。

0
user266926