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


21

PHPのrand()の出力はそのPRNGとして予測可能であることを多くのソースで読みましたが、それを事実として受け入れているのは、多くの場所で見たからです。

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

誰かが数千の推測内で与えられた瞬間にrand()を介してどのようなランダムな#が生成されたかを合理的に把握できますか?それとも10,000個の推測?どうやって?

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


「しかし、これがどのように予測できるか想像できない」まずen.wikipedia.org/wiki/Linear_congruential_generatorを読んで、どのように予測可能か想像できるようにします。その後、質問を修正して驚eliminateを取り除き、PHPのリバースエンジニアリングのより実用的な問題に移ります。 rand関数ソースがどのように機能するかを確認する
S.Lott

「これは潜在的なセキュリティホールだと思いましたか?」Evil Hackerがユーザーのランダムパスワードを取得できた場合にのみ、レインボーテーブルを使用してMD5ハッシュを元に戻し、元の(事前ハッシュ)値を復元し、次のパスワード要求を行ったことを保証します。理論的には可能だと思います。ただし、乱数用の有効なレインボーテーブルがある場合のみです。
-S.ロット

@ S.Lott-パスワードの問題ではありません。システムはパスワードをリセットし、URLで使用されるトークンをメールで送信します。トークンはMD5(rand())を介して生成されます。rand()の出力を予測できる場合、元のハッシュを持たずに、または元を知らなくても、だれかのパスワードを変更できます。
エリック

@エリック。右。「ランダムパスワード」を「ランダムトークン」に置き換えてください。誰かがMD5ハッシュを解いて乱数を回復し、次の乱数を確実に取得できる場合にのみ、トークンを悪用できます。次のランドを予測することは、ほんの一部です。MD5を元に戻すのは難しい部分です。
-S.ロット

1
MD5(rand())はrand()と同じセキュリティしか持たないことに注意してください。関係する非常に限られた数のセットに対してMD5(rand())-> rand()のルックアップテーブルを構築するのが実用的です。rand()の制限されたドメインでは、繰り返し試行を防止するメカニズムがない場合、単純なブルートフォースを試すことができます。
MZB

回答:


28

次の値を推測する機能は、randsrandが呼び出されたかを判断できることに関係しています。特に、事前に決められた数シードする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のバージョン*ほとんどのプラットフォームで**シーケンス97を生成します、97、39、77、93時にsrand「1024年とD。

明らかに、これはPHPの問題ではなく、これrand自体の実装の問題です。同じ問題は、Perlを含む同じ(または類似の)実装を使用する他の言語で発生します。

その秘trickは、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するつもりはありません。まあ、私の目が眩しくなり、狩りをやめることにしました。

することを示しているグーグルでのビットPHPの他の領域は、最高のランダム生成特性を有していない、との通話をするためにphp_combined_lcgここに目立つが、分析の特にこのビット:

この関数(gettimeofday)は、銀の大皿に正確なサーバータイムスタンプを返すだけでなく、「PHPから」「より多くのエントロピー」を要求すると、LCG出力を追加しuniqidます。

うんいますuniqid。の値は、2番目の引数を真の値に設定してphp_combined_lcg呼び出した後、結果の16進数を見ると見えるようuniqidです。

さて、私たちはどこにいましたか?

そうそう。 srand

そのため、ランダムな値を予測しようとしているコードがを呼び出さないsrand場合、によって提供される値を決定する必要がphp_combined_lcgありますuniqid。その値が手元にあれば、残りの値をブルートフォースすることが可能です- time()、PIDおよびいくつかの数学。リンクされたセキュリティの問題はセッションの切断に関するものですが、同じテクニックがここでも機能します。繰り返しますが、記事から:

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

必要に応じて、最後の手順を置き換えるだけです。

(このセキュリティの問題は、現在のバージョン(5.3.6)よりも以前のPHPバージョン(5.3.2)で報告されたため、動作が変更されuniqidたりphp_combined_lcg変更されたりする可能性があるため、この特定の手法は機能しなくなる可能性があります。 YMMV。)

一方、製品を作成しようとしているコードsrand手動呼び出した場合、の結果よりも何倍も優れたものを使用していない限り、php_combined_lcgおそらく値を推測してローカルにシードするのがはるかに簡単になります正しい番号のジェネレータ。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すべての呼び出しで、そのような彼らは常にことを再シードを。スホシンはサードパーティから提供されます。一部のLinuxディストリビューションでは、公式のPHPパッケージにデフォルトで含まれていますが、他のディストリビューションではオプションになっていますが、他のディストリビューションでは完全に無視されています。

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


チャールズに感謝します-あなたの答えとタンギュレナの線形合同発生器のリンクを読むことの間で、私はそれをよりよく把握していると感じています。この方法でrand()を使用するのは悪い考えであることはすでに知っていましたが、その理由はわかっています。
エリック

うわー、完全によく書かれた答えの小道具、ありがとう!
デビッドホブス14年

10

rand()関数がどれほど非ランダムであるかを視覚的に示すために、すべてのピクセルが「ランダム」な赤、緑、青の値で構成されている画像を次に示します。

ランダムなRGB値

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

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

両方とも暗号的に安全ではなく、予測可能な結果を​​生成することに注意してください。


7

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

それは線形合同ジェネレータです。これは、実質的に次の機能があることを意味しますNEW_NUMBER = (A * OLD_NUMBER + B) MOD C。NEW_NUMBERとOLD_NUMBERをグラフ化すると、斜めの線が見え始めます。PHPのRANDドキュメントに関する注意事項のいくつかは、その方法の例を示しています。

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

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

誰かが数千の推測内で与えられた瞬間にrand()を介してどのようなランダムな#が生成されたかを合理的に把握できますか?それとも10,000個の推測?どうやって?

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

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

フィードではmd5が破損していると考えられるため、SHA-2ファミリの何かを使用することをお勧めします。一部の人はgoogleを使用してmd5ハッシュを解読します。何かをハッシュし、そのハッシュをグーグル検索に投げ込むだけです。基本的にグーグルは巨大なレインボーテーブルになりました。


1

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


1
次の数字は完全に決定論的だと思います。「比較的」ではなく、絶対に。擬似乱数ジェネレーターの問題は、シーケンスが統計テストに合格することです。2つの隣接する数値は、完全に決定論的ですが、実際の乱数と共通の統計的特性を持つ場合があります。
-S.ロット

1
次の数値は完全に決定的です。それが、疑似乱数ジェネレーターの「疑似」の意味です。一方、次の番号が実際に取得することはほとんど不可能であることを決定するために必要な情報。
ライン

@ S.Lott-2 ^ 32の可能な出力に数字が複数回現れる可能性があり、表示されるたびに異なる数字が続く可能性があるという印象を受けました。しかし、Xのシードが与えられ、Yの結果を返すと、次の結果は常に同じになります。したがって、実際には、Yに続く数個の数字があるかもしれません。しかし、私は間違っているかもしれません。PRNGを実際に見てから長い時間がかかりました。
pdr
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.