PHP: openssl_random_pseudo_bytes と mcrypt_create_iv の比較

ランダムな文字列を生成するにはファイル関数を使って /dev/urandom に直接アクセスするやりかたがもっとも明快であり、OS のしくみを学ぶ機会を提供するので、教育的でもある。徳丸本でもそのやりかたが採用されている。

ただし、open_basedir の制約で /dev/urandom にアクセスできない場合や、Windows では /dev/urandom が使えない場合があるので、Windows で開発し Linux で運用するユースケースを考慮するのであれば、関数の利用のほうがのぞましいと考えられる。

PHP 5.3 では openssl_random_pseudo_bytes が追加され、また mcrypt_create_iv の第2引数の選択肢に MCRYPT_DEV_URANDOM が追加され、どちらの関数およびライブラリでは内部処理において Unix 系の場合は /dev/urandom、Windows の場合は CryptGenRandom が使われる。

そのほかのやりかたについては PHP コアの RFC である Platform and extension-independent API to the system CSPRNG やアテネ大学の「I Forgot Your Password: Randomness Attacks Against PHP Applications」の 2.2 Randomness Generation にまとめられている。

これらの関数が実際に使われているケースとして PHP 5.5 で導入されたパスワードハッシュ API のユーザー関数版の password_compat、Symfony2 の Security コンポーネント (openssl_random_pseudo_bytes のみ)、Zend Framework の Math、「 I Forgot Your Password: Randomness Attacks Against PHP Applications」の筆者達が公開しているSecure-random-bytes-in-PHPなどをあげることができる。

知名度や用途の面で openssl_random_pseudo_bytes のほうが mcrypt_create_iv よりも利用できる環境が多いと考えられるが、考慮する必要があることが2つある。

まずは PHP 5.3.4 以前は Windows 環境で処理速度が遅い問題があること。原因は Windows 版の処理で使われる RAND_screen にある。

もう1つは Unix 環境の処理に使われる RAND_pseudo_bytes の man によれば予測不可能ではないので、用途はかぎられると書かれていること。ただし、Tor のチーフアーキテクトの方のコメントによれば RAND_pseudo_bytes の実装である ssleay_rand_pseudo_bytes は RAND_bytes を呼び出しているので、現在の実装にはセキュリティホールは存在しないとのことだが、誰かが RNG 生成器を書き換えたり、将来、OpenSSL が安全ではない実装に変更する可能性を考慮すると、RAND_bytes に置き換えるべきだとしている。PHP 5.5 でパスワード API を導入した @ircmaxell 氏は Random Number Generation In PHP の記事のコメント欄で mcrypt_create_iv のほうをすすめ、RAND_bytes に対応した API を導入すべきという意見を述べている。

ほかの言語の動向に関して、Ruby 1.9 は RAND_bytes を内部実装に使い、Python 3.3 で ssl.RAND_bytes、ssl.RAND_pseudo_bytes が導入された。

Ruby のコア開発者による Random クラスのレビューを読むとよい名前が思い浮かばないことがこれまで PHP に疑似乱数の標準 API 導入が進まなかった理由の1つとして挙げることができるのではないかと思った。

最後に、その他の資料として RFC 1750(1994年) の後継であるRFC 4086(2005年) (Randomness Requirements for Security (セキュリティのための乱雑性についての要件)) やその解説のリンクを記録しておく。暗号論はあまり勉強していないので、時間があるときに少しずつ読みたい。