web-dev-qa-db-ja.com

PHPのRand()の出力を予測する

PHPのRand()の出力はPRNGとして予測可能であることを数多くのソースで読みましたが、私は多くの場所でそれを目にしたことがあるという理由だけで、それを事実として受け入れています。

概念実証に興味があります。Rand()の出力を予測するにはどうすればよいですか?読んだところ この記事 乱数はポインタ(シード)から始まるリストから返される数値であることは理解しています-しかし、これがどのように予測可能であるかは想像できません。

誰かが、数千回の推測の中で、Rand()を介して生成されたランダムな番号を適度に理解できるでしょうか?それとも10,000推測ですか?どうやって?

これは、Rand()を使用してパスワードを失ったユーザーのトークンを生成する認証ライブラリを見たので、これが発生し、これが潜在的なセキュリティホールであると想定しました。その後、メソッドをopenssl_random_pseudo_bytes()、元のハッシュされたパスワード、およびマイクロタイムの混合物をハッシュするように置き換えました。これを行った後、外を見ていると、Rand()のmd5であることを知っていても、トークンを推測する方法がわかりません。

21
Erik

Randから次の値を推測する機能は、srandが何で呼び出されたかを判別できることに関連しています。特に、srandに所定の数をシードすると、予測可能な出力が得られます! PHPインタラクティブプロンプトから:

[charles@charles-workstation ~]$ php -a
Interactive Shell

php > srand(1024);
php > echo Rand(1, 100);
97
php > echo Rand(1, 100);
97
php > echo Rand(1, 100);
39
php > echo Rand(1, 100);
77
php > echo Rand(1, 100);
93
php > srand(1024);
php > echo Rand(1, 100);
97
php > echo Rand(1, 100);
97
php > echo Rand(1, 100);
39
php > echo Rand(1, 100);
77
php > echo Rand(1, 100);
93
php > 

これは一部のまぐれだけではありません。ほとんどのPHPバージョン* ほとんどのプラットフォーム** 1024でsrand 'dした場合、シーケンス97、97、39、77、93が生成されます。

明確に言うと、これはPHPの問題ではなく、Rand自体の実装の問題です。同じ問題は、Perlを含む同じ(または同様の)実装を使用する他の言語でも発生します。

トリックは、PHPの正常なバージョンには、「不明な」値がsrandに事前にシードされているということです。ああ、それは本当にではありません=不明。ext/standard/php_Rand.hから:

#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))

したがって、これはtime()、PID、およびphp_combined_lcgで定義されているext/standard/lcg.cの結果を含むいくつかの数学です。私はここでc&pをするつもりはありません。まあ、目がつやつやして、狩りをやめることにしました。

Googlingのビットは PHPの他の領域には最高のランダムネス生成プロパティがない を示し、php_combined_lcgの呼び出し、特にこの分析のビットはここで際立っています:

この関数(gettimeofday)は、Silver Platter上の正確なサーバータイムスタンプを返すだけでなく、「PHPのuniqidから」「エントロピーの増加」を要求した場合にLCG出力を追加します。

ええ that uniqidphp_combined_lcgの値は、2番目の引数をtrue値に設定してuniqidを呼び出した後の結果の16進数字を見るとわかります。

さて、どこにいたの?

そうそう。 srand

したがって、ランダムな値を予測しようとしているコードがdoes n't call srandの場合、php_combined_lcgによって提供される値を判別する必要があります。 (間接的に?)uniqidの呼び出しを通じて。その値が手元にあれば、残りの値-time()、PID、およびいくつかの計算を総当たりにするのはfeasibleです。リンクされているセキュリティの問題はセッションの中断に関するものですが、同じテクニックがここでも機能します。繰り返しますが、記事から:

上記の攻撃手順の概要は次のとおりです。
  • サーバーが再起動するのを待ちます
  • uniqid値をフェッチする
  • これからRNGシードをブルートフォース
  • オンラインステータスをポーリングして、ターゲットが表示されるのを待ちます
  • ステータスポーリングをuniqidポーリングとインターリーブして、現在のサーバー時間とRNG値を追跡します。
  • ポーリングで確立された時間とRNG値の間隔を使用してサーバーに対してブルートフォースセッションID

必要に応じて、最後のステップを置き換えるだけです。

(このセキュリティの問題は、現在のバージョン(5.3.6)よりも前のPHPバージョン(5.3.2)で報告されているため、uniqidおよび/またはphp_combined_lcgが変更されたため、このspecific技法はもう機能しない可能性があります。YMMV。)

一方、生成しようとしているコード手動でsrandを呼び出すの場合、php_combined_lcgの結果よりも何倍も優れたコードを使用していない限り、おそらく値を推測し、ローカルジェネレータに適切な数をシードするための時間easierが多くなります。手動でsrandを呼び出すほとんどの人も、これがどれほどひどい考えであるかを理解していないため、より良い値を使用する可能性はほとんどありません。

mt_Randも同じ問題に悩まされていることは注目に値します。既知の値でmt_srandをシードすると、予測可能な結果も生成されます。エントロピーをopenssl_random_pseudo_bytesに基づくことは、おそらくより安全な賭けです。

tl; dr:最良の結果を得るには、PHP乱数ジェネレーターをシードしないでください。 、uniqidをユーザーに公開しないでください。これらのいずれかまたは両方を実行すると、乱数が推測されやすくなる場合があります。


PHP 7の更新:

PHP 7.0では、コア関数として random_bytesrandom_int が導入されています。基盤となるシステムのCSPRNG実装を使用するため、シードされた乱数ジェネレーターが抱える問題から解放されます。これらは、実際にopenssl_random_pseudo_bytesに似ていますが、拡張機能をインストールする必要がありません。 ポリフィルはPHP5で利用可能


*: Suhosinセキュリティパッチ は、Randおよびmt_Randの動作を変更して、すべての呼び出しで常に再シードされるようにします。 Suhosinはサードパーティによって提供されます。一部のLinuxディストリビューションでは、デフォルトで公式のPHPパッケージに含まれていますが、他のディストリビューションではオプションになっているものもあれば、完全に無視しているものもあります。

**:使用されているプラ​​ットフォームと基になるライブラリコールに応じて、ここに記載されているものとは異なるシーケンスが生成されますが、Suhosinパッチを使用しない限り、結果は再現可能です。

28
Charles

Rand() functionがランダムでないことを視覚的に示すために、すべてのピクセルが「ランダムな」赤、緑、青の値で構成されている画像を次に示します。

Random RGB values

通常、画像にパターンはありません。

異なる値でsrand()を呼び出してみましたが、この関数の予測可能性は変わりません。

どちらも暗号的に安全ではなく、予測可能な結果を​​生み出すことに注意してください。

10
minipif

PHPのRand()の出力は、PRNGとして予測可能です

線形合同ジェネレーター です。つまり、効果的な関数があることを意味します:NEW_NUMBER = (A * OLD_NUMBER + B) MOD C。 NEW_NUMBERとOLD_NUMBERのグラフを作成すると、斜めの線が見え始めます。 PHPのRandのドキュメント に関する注記の一部は、その方法の例を示しています。

これは、Rand()を使用してパスワードを失ったユーザーのトークンを生成する認証ライブラリを見たために発生し、これが潜在的なセキュリティホールであると想定しました。

Windowsマシンでは、Randの最大値は2 ^ 15です。これにより、攻撃者が確認できる可能性は32,768のみになります。

誰かが、数千回の推測の範囲内のある瞬間にRand()を介してどのランダム番号が生成されたかを合理的に理解できますか?それとも10,000推測ですか?どうやって?

この記事 はまさにあなたが探しているものではありませんが、一部の研究者が乱数ジェネレータの既存の実装を採用し、それを使用してテキサスホールデムでお金を稼ぐ方法を示しています。 52があります!デッキをシャッフルする可能性がありますが、実装では32ビットの乱数ジェネレータ(Windowsマシンではmt_getrandmaxからの最大数)を使用し、深夜からのミリ秒単位の時間をシードしました。これにより、シャッフル可能なデッキの数が約2 ^ 226から約2 ^ 27に減少し、リアルタイムで検索して、どのデッキが処理されたかを知ることができます。

これを行った後、外を見ていると、Rand()のmd5であることを知っていても、トークンを推測する方法がわからないことに気付きました。

連邦機関はmd5が壊れていると見なしているので、 SHA-2 ファミリーの何かを使用することをお勧めします。一部の人々は、md5ハッシュが非常に一般的であるため、googleを使用してmd5ハッシュを復号化します。何かをハッシュし、そのハッシュをグーグル検索に投げ込むだけです-基本的にグーグルは巨大になりました レインボーテーブル

7
Tangurena

ランダムに生成された数が与えられた場合、次の数は比較的予測可能であると言う方が実際にはより正確です。できる数は非常に多くあります。しかし、それはあなたがそれを推測できることを意味するのではなく、あなたがそれを行うプログラムをかなり速く書くことができるということではありません。

1
pdr