web-dev-qa-db-ja.com

PHPでの安全な乱数生成

使用例:[パスワードを忘れた]ボタン。ユーザーの元のパスワードはハッシュ形式で保存されているため、見つかりません。そのため、新しいランダムなパスワードを生成し、電子メールで送信するだけです。これには暗号的に予測不可能な乱数が必要ですが、mt_Randでは十分ではありません。一般に、ホスティングサービスがオペレーティングシステムへのアクセスを提供して暗号化乱数モジュールなどをインストールすることはできないため、方法を探しています。 PHP自体に安全な乱数を生成します。

これまでに思いついた解決策は、最初のシードを保存し、次に各呼び出しについて、

result = seed
seed = sha512(seed . mt_Rand())

これは、sha512ハッシュ関数のセキュリティに基づいています(mt_Rand呼び出しは、データベースのコピーを取得する敵対者を少し難しくするためです)。

何か不足していますか、それともよりよく知られている解決策はありますか?

34
rwallace

OpenSSL openssl_random_pseudo_bytesの使用を検討することもできます。これは、PHP 5.3以降で利用可能です。

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )

疑似ランダムバイトの文字列を生成します。バイト数はlengthパラメータで決定されます。また、暗号的に強力なアルゴリズムを使用して疑似ランダムバイトを生成したかどうかを示し、オプションのcrypto_strongパラメータを介してこれを行います。これがFALSEになることはまれですが、一部のシステムが壊れているか古い可能性があります。

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

PHP 7なので、random_bytes関数が利用可能

string random_bytes ( int $length )

http://php.net/manual/en/function.random-bytes.php

40
Eugene Dounar

パスワードのエントロピーソースとして、UNIXシステムでは/ dev/urandomを、Windowsプラットフォームではcrypto-apiをターゲットにすることを強くお勧めします。

ハッシュを実現することの重要性は、魔法のエントロピー増加デバイスではありません。この方法でそれらを誤用することは、ハッシュされる前にシードとRand()データを使用することと同じくらい安全ではありません。シードは相殺(確定的mt_Rand())するため、シードを含めてもまったく意味がありません。

人々は自分たちが賢くて賢いと思っており、彼らの労働の結果は彼らのシステムのセキュリティと他のシステムのセキュリティを(不十分なアドバイスを介して)不必要な危険にさらしている脆弱なシステムとデバイスです。

二つの間違いは正しいことにはなりません。システムは、その最も弱い部分と同じくらい強力です。これは、それをさらに不安定にすることを受け入れる免許や言い訳ではありません。


以下は、いくつかのPHPからセキュアなランダム128ビット文字列を取得するためのコードです このコメントはphp.netのMark Seecofによる

「セキュリティまたは暗号化の目的でいくつかの疑似ランダムビットが必要な場合(たとえば、ブロック暗号にはランダムIV、パスワードハッシュにはランダムソルト)mt_Rand()は不十分なソースです。ほとんどのUnix/Linuxおよび/またはMS-Windowsプラットフォームでは、次のように、OSまたはシステムライブラリからのより良いグレードの疑似ランダムビット:

<?php
// get 128 pseudorandom bits in a string of 16 bytes

$pr_bits = '';

// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
    $pr_bits .= @fread($fp,16);
    @fclose($fp);
}

// MS-Windows platform?
if (@class_exists('COM')) {
    // http://msdn.Microsoft.com/en-us/library/aa388176(VS.85).aspx
    try {
        $CAPI_Util = new COM('CAPICOM.Utilities.1');
        $pr_bits .= $CAPI_Util->GetRandom(16,0);

        // if we ask for binary data PHP munges it, so we
        // request base64 return value.  We squeeze out the
        // redundancy and useless ==CRLF by hashing...
        if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }
    } catch (Exception $ex) {
        // echo 'Exception: ' . $ex->getMessage();
    }
}

if (strlen($pr_bits) < 16) {
    // do something to warn system owner that
    // pseudorandom generator is missing
}
?>

注意:一般的に、/ dev/urandomを読み取る試みとCAPICOMにアクセスする試みの両方をコードに残しても安全ですが、それぞれが他のプラットフォームで静かに失敗します。両方ともそのままにしておくと、コードの移植性が高まります。」

59
Einstein

PHPには、CSPRNG関数の新しいセット(random_bytes()およびrandom_int())が同梱されています。後者の関数を文字列ジェネレーター関数に変換するのは簡単です。

<?php
/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    $length,
    $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    if ($max < 1) {
        throw new Exception('$keyspace must be at least two characters long');
    }
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

これをPHP 5プロジェクトで使用する必要がある場合は、これらの関数のポリフィルである random_compat のコピーを自由に入手してください。

7