暗号的に安全なトークンの生成


83

APIにアクセスするための32文字のトークンを生成するために、現在使用しているものは次のとおりです。

$token = md5(uniqid(mt_rand(), true));

この方法はシステムクロックに基づいているため、暗号的に安全ではないことを読みましたopenssl_random_pseudo_bytes。予測が難しいため、より良い解決策になります。

この場合、同等のコードはどのようになりますか?

私はこのようなことを推測しますが、これが正しいかどうかはわかりません...

$token = md5(openssl_random_pseudo_bytes(32));

また、関数に渡す必要がある長さはどれくらいですか?


4
なぜmd5なのか?バイトストリームを16進数に変換するだけです。openssl_random_pseudo_bytes()から32バイトが返され、PHPドキュメントの例に示すようにbin2hex()を使用してこれらの各バイトを16進値としてレンダリングします
Mark Ba​​ker

32文字だけ欲しいですか?どうすればいいですか?
発砲

10
md5()32文字の文字列を生成しますが、128ビット相当のデータしか含まれていません。openssl_random_pseudo_bytes()真のバイナリデータを返すため、32 * 8 = 256ビットのランダム性があります。32バイトのランダムな文字列をmd5に詰め込むことで、その一意性を大幅に削減できます。
マークB

1
それで$token = bin2hex(openssl_random_pseudo_bytes(16));十分ですか、それとも長さとして1を渡し、16進数で文字列に追加する16回の反復のループが必要ですか?
発砲

16バイトを16進数に変換した最終的な解決策は正しいです。ただし、ここではOpenSSLに実際に依存するべきではありません#1 #2 #3。PHP 7.0以降を使用するとすぐに、それを使用する言い訳はもうありません。OpenSSLと16進エンコーディングの代わりに、を試してくださいRandom::alphanumericString($length)。これは、これらの16文字に約20億倍の「エントロピー」を収めます。
CAW

回答:


187

正しい解決策は次のとおりです。

$token = bin2hex(openssl_random_pseudo_bytes(16));

# or in php7
$token = bin2hex(random_bytes(16));

1
CryptoLibでopenssl_random_pseudo_bytesを使用して一意のトークンを生成する方法に関する別の回答を追加しました。これにより、0〜9のAFだけでなく、すべての文字でトークンを生成できます。
mjsa 2014

4
PHP 5.xによるrandom_bytes()およびrandom_int()のサポートgithub.com/paragonie/random_compat
MTK

4
また、場合がありますbin2hex(random_bytes($length/2))文字の数は常に二重になりますので、あなたはすべてのバイトのための2進文字を持つことができるので、$lengthそれなし
Aフレンド

3
@AFriend同意しました。$length(バイトサイズを気にするのではなく)文字数の文字列を生成する関数を作成する場合は、を渡す必要がありますrandom_bytes($length/2)
BadHorsie

10

openssl_random_pseudo_bytesを使用する場合は、CrytoLibの実装を使用するのが最適です。これにより、すべての英数字を生成できます。openssl_random_pseudo_bytesの周りにbin2hexを貼り付けると、AF(キャップ​​)と数字が表示されます。

path / to /をcryptolib.phpファイルを配置した場所に置き換えます(GitHubのhttps://github.com/IcyApril/CryptoLibにあります

<?php
  require_once('path/to/cryptolib.php');
  $token = IcyApril\CryptoLib::randomString(16);
?>

CryptoLibの完全なドキュメントは、https://cryptolib.ju.je/で入手できます。他の多くのランダムな方法、カスケード暗号化、ハッシュの生成と検証について説明します。しかし、あなたがそれを必要とするならば、それはそこにあります。


同じバイト数の場合:openssl_random_pseudo_bytes(16)各バイトは任意の値(0〜255)をrandomString(16)持つことができますが、各バイトは1バイトあたり62の組み合わせであるaz Az 0〜9のみを持つことができます。bin2hexは非効率的なエンコーディング(50%)であるため、文字列の長さごとにrandomStringの方が優れています。よりよい使用するbase64_encode(openssl_random_pseudo_bytes(16))(必要に応じてこの場合16が、ALTER)短い文字列と正確な既知のセキュリティのバイト数の
ロドニー

1
@Rodneyベアは心の底では64も含む文字列を生成します+/そして=あなたはそれが理想的ではありません(例えばAPIキー)ユーザーフレンドリーなトークンを生成しようとしているそうだとすれば、文字が。
BadHorsie

9

暗号的に安全な乱数ジェネレーターがある場合は、その出力をハッシュする必要はありません。実際、あなたはしたくありません。使用するだけ

$token  = openssl_random_pseudo_bytes($BYTES,true)

ただし、$ BYTESは、必要なデータのバイト数です。MD5には128ビットのハッシュがあるため、16バイトで十分です。

ちなみに、元のコードで呼び出す関数はどれも暗号的に安全ではありません。ほとんどの関数は、安全な他の関数と組み合わせても、1つだけを使用すると安全でなくなるほど有害です。MD5にはセキュリティの問題があります(ただし、このアプリケーションでは関連性がない場合があります)。Uniqidは、デフォルトで暗号的にランダムなバイトを生成しないだけでなく(システムクロックを使用するため)、渡される追加のエントロピーは、暗号的に安全ではない線形合同ジェネレーターを使用して結合されます。実際、サーバークロックの値がわからなくても、いくつかのAPIキーにアクセスできるすべてのAPIキーを推測できる可能性があります。最後に、追加のエントロピーとして使用するmt_rand()も、安全な乱数ジェネレーターではありません。


27
openssl_random_pseudo_bytes()の2番目の引数は、参照によって渡される変数である必要があります。openssl_random_pseudo_bytesの後、opensslが暗号的に強力なアルゴリズムを使用できた場合、その変数はtrueまたはfalseになります。とにかく実行しようとする暗号的に強力なアルゴリズムを使用するようにopensslに指示するためには使用されません。
Jonathan Amend

-1

信頼できるパスワードASCII文字a〜zA〜Zおよび数字0〜9からのみ作成できます。その最善の方法は、PHP7のrandom_int()random_bytes()などの暗号的に安全なメソッドのみを使用することです。base64_encode()としての残りの関数文字列の信頼性を高めASCII文字に変更するためのサポート関数としてのみ使用できます。

mt_rand()は安全ではなく、非常に古いものです。任意の文字列からrandom_int()を使用する必要があります。バイナリ文字列からバイナリ文字列を信頼できるものにするためにbase64_encode()またはbin2hexを使用する必要がありますが、そうすると、バイトは16の位置(値)にのみカットされます。 この関数の私の実装を参照してください。すべての言語を使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.