
この記事では、暗号化で使用される乱数の生成に関連する問題を分析します。 PHP5は暗号乱数を生成するための単純なメカニズムを提供しませんが、PHP7はCSPRNG関数を導入することでこの問題を解決します。
CSPRNGとは何ですか?
ウィキペディアを引用すると、暗号的に安全な擬似乱数ジェネレーター(CSPRNG)は、暗号化で使用できる特定のプロパティを持つ擬似乱数ジェネレーターです。
CSPRNGは、主に次の目的で使用されます。
- 鍵の生成(公開/秘密鍵の生成を含む)
- ユーザーアカウントのランダムパスワードを作成する
- 暗号化システム
高レベルのセキュリティを維持する主な側面は、高品質のランダム性です。
PHP7のCSPRNG
PHP7は、
random_bytes
に使用できる2つの新しい関数
random_bytes
と
random_int
ます。
random_bytes
関数は文字列を返し、入力パラメーターとして戻り値の長さ(バイト単位)を指定する
int
を受け取ります。
$bytes = random_bytes(10); var_dump(bin2hex($bytes)); //possible ouput: string(20) "7dfab0af960d359388e6"
random_int
は、指定された範囲の整数を返します。
var_dump(random_int(1, 100)); //possible output: 27
オフスクリーン
上記の関数のランダム性の原因は、環境によって異なります。
- Windowsでは、
CryptGenRandom();
が常に使用されCryptGenRandom();
- 他のプラットフォームでは-利用可能な場合、
arc4random_buf()
が関係します(BSD派生システムまたはlibbsd
使用するシステムの場合はtrue)。 - 上記が利用できない場合、Linuxはシステム
getrandom(2)
を使用します。 - これらすべてが失敗した場合、PHPは
/dev/urandom
を最終試行として使用しようとします。 - これらのソースを使用できない場合、エラーがスローされます。
簡単なテスト
優れた乱数生成システムは、世代の「品質」によって決まります。 それをテストするために、統計テストのセットがしばしば使用され、統計の複雑なトピックを掘り下げることなく、既知の参照動作をジェネレータの結果と比較し、その品質を評価するのに役立ちます。
最も簡単なテストの1つはサイコロです。 1つのボーンで6つが脱落する確率は6つに1つですが、3つのサイコロを100回投げると、1、2、および3つの6の予想される損失は次のようになります。
- 0 6 = 57.9回
- 1 6 = 34.7回
- 2 6 = 6.9回
- 3つのシックス= 0.5回
サイコロのロールを1,000,000回再生するコードは次のとおりです。
$times = 1000000; $result = []; for ($i=0; $i < $times; $i++) { $dieRoll = array(6 => 0); //initializes just the six counting to zero $dieRoll[roll()] += 1; //first die $dieRoll[roll()] += 1; //second die $dieRoll[roll()] += 1; //third die $result[$dieRoll[6]] += 1; //counts the sixes } function roll() { return random_int(1,6); } var_dump($result);
random_int
と単純な
rand
を使用して
random_int
コードを実行
random_int
次の結果が
random_int
ます。
シックス | 期待される結果 | random_int | ランド |
---|---|---|---|
0 | 579000 | 579430 | 578179 |
1 | 347000 | 346927 | 347620 |
2 | 69000 | 68985 | 69586 |
3 | 5000 | 4658 | 4615 |
rand
と
random_int
よりよく比較するために、式
PHP
-
/
sqrt( )
を使用して結果をプロットします。
グラフは次のようになります(ゼロに近いほど良い):

3つの6での貧弱な結果とテストの単純さにもかかわらず、
rand
よりも
random_int
明らかな優位性があります。
PHP5はどうですか?
デフォルトでは、PHP5は強力な擬似乱数ジェネレーターを提供しません。 しかし、実際には
openssl_random_pseudo_bytes()
、
mcrypt_create_iv()
または直接
/dev/random
または
/dev/urandom
と
fread()
を使用するなど、いくつかのオプションがあります。 RandomLibやlibsodiumなどのライブラリもあります。
適切な乱数ジェネレーターの使用を開始すると同時に、PHP7に切り替える準備がまだ整っていない場合は、Paragon Initiative Enterprisesの
random_compat
ライブラリを使用できます。 PHP 5.xプロジェクトで
random_bytes()
および
random_int()
を使用できます。
ライブラリはComposerからインストールできます。
composer require paragonie/random_compat
require 'vendor/autoload.php'; $string = random_bytes(32); var_dump(bin2hex($string)); // string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f" $int = random_int(0,255); var_dump($int); // int(81)
PHP7と比較して、
random_compat
はわずかに異なる優先順位を使用します。
-
fread()
/dev/urandom
利用可能な場合は/dev/urandom
-
mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
-
COM('CAPICOM.Utilities.1')->GetRandom()
-
openssl_random_pseudo_bytes()
この順序がドキュメントで使用されている理由に関する詳細情報を読むことができます 。
ライブラリを使用してパスワードを生成する例:
$passwordChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $passwordLength = 8; $max = strlen($passwordChar) - 1; $password = ''; for ($i = 0; $i < $passwordLength; ++$i) { $password .= $passwordChar[random_int(0, $max)]; } echo $password; //possible output: 7rgG8GHu
簡単な要約
暗号的に
random_compat
擬似乱数ジェネレーターを常に使用する必要があり、
random_compat
はこれに適したソリューションです。
信頼性の高いランダムデータのソースが必要な場合は、
random_int
および
random_bytes
に
random_int
して
random_bytes
。