PHP'nin rand çıktısını tahmin etmek ()


21

PHP'nin rand () çıktısının bir PRNG olarak tahmin edilebildiğini sayısız kaynakta okudum ve çoğunlukla bunu pek çok yerde gördüğüm için gerçeği kabul ediyorum.

Kavram ispatıyla ilgileniyorum: rand () 'ın çıktısını öngörmek konusunda nasıl gidebilirim? Bu makaleyi okuduğumda rastgele sayının bir göstergeden (tohumdan) başlayan bir listeden döndürülen bir sayı olduğunu anlıyorum - ama bunun nasıl tahmin edilebilir olduğunu hayal edemiyorum.

Birisi, birkaç bin tahmin içinde, belirli bir anda rand () aracılığıyla hangi rasgele # üretildiğini makul bir şekilde anlayabilir mi? hatta 10.000 tahmin? Nasıl?

Bu, şifre kaybettiren kullanıcılar için bir belirteç üretmek için rand () kullanan bir kimlik kütüphanesi gördüğümden ve bunun potansiyel bir güvenlik sorunu olduğunu varsaydım. O zamandan beri metodu, karma openssl_random_pseudo_bytes()karma şifresini ve mikrotime karışımını değiştirdim . Bunu yaptıktan sonra dışarıya bakacak olursam, jetonun md5 olduğunu bile bilsem bile belirteci nasıl tahmin edeceğimi bilemedim.


"ama bunun nasıl tahmin edilebilir olduğunu hayal edemiyorum"? Öncelikle " en.wikipedia.org/wiki/Linear_congruential_generator " sayfasını okumanız gerekir, böylece ne kadar tahmin edilebilir olduğunu hayal etmeye başlayabilirsiniz. Daha sonra şaşkınlığı ortadan kaldırmak ve PHP'nin ters mühendislik uygulamalarında daha pratik konulara geçmek için sorunuzu gözden geçirebilirsiniz. nasıl çalıştığını görmek için rand fonksiyon kaynağı
S.Lott

"Bunun potansiyel bir güvenlik boşluğu olduğunu varsaydım"? Yalnızca Evil Hacker bazı kullanıcıların rasgele şifresini alabiliyorsa, orijinal (hash öncesi) değeri kurtarmak için MD5 karma değerini geri almak için bir gökkuşağı tablosu kullanın ve ardından bir sonraki şifre talebini yaptıklarını garanti edin. Teorik olarak mümkün sanırım. Ancak sadece rasgele bir sayı için çalışan bir gökkuşağı masa vardı.
S.Lott

@ S.Lott - bu bir şifre meselesi değil. Sistem şifreyi sıfırlamanıza izin verir ve size bir URL'de kullanılan bir jetonu e-postayla gönderir. Belirteç MD5 (rand ()) ile üretilir. Rand () işlevinin çıktısını tahmin edebiliyorsanız, orijinalin özeti olmadan veya orijinali tanımadan herkesin şifresini değiştirebilirsiniz.
Erik,

@Erik. Sağ. Bu yardımcı olursa, "rastgele şifre" yerine "rastgele belirteç" yazın. Belirteç yalnızca, birileri MD5 karmaşasını rasgele sayıyı kurtarmak için gevşetirse ve bir sonraki rasgele sayıyı alacağından emin olarak kötüye kullanılabilir. Bir sonraki randın tahmini sadece küçük bir kısımdır. MD5'i geri almak zor kısımdır.
S.Lott

1
MD5 (rand ()) 'in yalnızca rand () ile aynı güvenceye sahip olduğunu unutmayın. Çok sınırlı sayıda numara için MD5 (rand ()) -> rand () arama tablosu oluşturmak pratiktir. Rand () 'nin sınırlı alanı ile, tekrarlanan girişimleri önleyen bir mekanizma olmadığı sürece basit kaba kuvveti deneyebilirsiniz.
MZB,

Yanıtlar:


28

Bir sonraki değeri tahmin etme yeteneği, randneyin neyle srandçağrıldığını belirleyebilme becerisine bağlıdır . Özellikle, önceden belirlenmiş bir sayıyla tohumlama srandöngörülebilir çıktı ile sonuçlanır ! PHP etkileşimli komut isteminden:

[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 > 

Bu sadece bir şans değil. Çoğu platformda ** çoğu PHP sürümü * , 1024'te deyince 97, 97, 39, 77, 93 dizisini oluşturur .srand

Açıkçası, bu PHP ile ilgili bir sorun değil, bu randkendi uygulamasında bir sorun . Aynı sorun, Perl de dahil olmak üzere aynı (veya benzer) uygulamayı kullanan diğer dillerde de görülür.

İşin püf noktası, PHP'nin aklı başında herhangi bir sürümünün srand"bilinmeyen" bir değerle önceden ekilmiş olacağıdır . Oh, ama gerçekten bilinmiyor. Kimden ext/standard/php_rand.h:

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

Yani, time()PID php_combined_lcgile tanımlanmış olan ve bunun sonucu olan bazı matematik ext/standard/lcg.c. Burada c & p gitmeyeceğim, gözlerim de parlıyordu ve avlanmayı bırakmaya karar verdim.

Bir miktar Google, PHP'nin diğer alanlarının en iyi rastgelelik oluşturma özelliklerine sahip olmadığını gösterir ve php_combined_lcgburada özellikle bu analizde öne çıkmaya çağırır :

Bu işlev ( gettimeofday) bize sadece gümüş bir tepside kesin bir sunucu zaman damgasını geri vermekle kalmaz , ayrıca "daha entropi" (PHP'den uniqid) talep edersek LCG çıktısı da ekler .

Evet buuniqid . Değeri görünüyor php_combined_lcgbiz çağrıldıktan sonra ortaya çıkan altıgen basamak baktığımızda gördüğümüz şeydir uniqidgerçek bir değere ikinci argüman setiyle.

Şimdi neredeydik?

Oh evet. srand.

Kod rasgele değerleri tahmin etmeye çalışıyorsanız Yani, yok diyoruz srand, sen sağladığı değerini belirlemek için ihtiyaç gidiyoruz php_combined_lcgalabilirsiniz, hangi (dolaylı?) Bir çağrı yoluyla uniqid. Bu değer el ile, değerin geri kalanını kaba bir şekilde zorlamak mümkündür - time()PID ve biraz matematik. Bağlantılı güvenlik sorunu oturumları kırmakla ilgili, ancak aynı teknik burada işe yarayacak. Yine, makaleden:

İşte yukarıda özetlenen saldırı adımlarının bir özeti:
  • sunucunun yeniden başlatılmasını bekleyin
  • uniqid değeri almak
  • RNG tohumunu bu durumdan kaba kuvvet
  • hedefin görünmesini beklemek için çevrimiçi durumu yokla
  • Mevcut sunucu saatini ve RNG değerini takip etmek için unidid anketlerle interleave durum anketleri
  • sorgulamada oluşturulan zaman ve RNG değer aralığını kullanan sunucuya karşı kaba kuvvet oturumu kimliği

Sadece bu son adımı gerektiği gibi değiştirin.

(Bu güvenlik sorunu, şu anda sahip olduğumuzdan (5.3.6) daha önceki bir PHP sürümünde (5.3.2) bildirilmiştir, bu nedenle davranışının uniqidve / veya davranışının php_combined_lcgdeğişmesi olasıdır , bu nedenle bu teknik artık uygulanabilir olmayabilir. YMMV.)

Öte yandan, üretmeye çalıştığınız kod el ile çağırıyorsasrand , sonuçtan çok daha iyi bir şey kullanmıyorlarsa , değeri tahmin etmek ve yerelinizi tohumlamak için php_combined_lcgçok daha kolay bir zaman geçirirsiniz. Doğru numaraya sahip jeneratör. El ile arayacak olan çoğu insan, bunun srandbir fikirden ne kadar korkunç olduğunu ve bu nedenle daha iyi değerler kullanması muhtemel olmadığını anlamaz.

mt_randAynı sorundan da etkilendiğine dikkat çekmek önemlidir. mt_srandBilinen bir değerde tohumlama da öngörülebilir sonuçlar üretecektir. Entropininizden vazgeçmek openssl_random_pseudo_bytesmuhtemelen daha güvenli bir bahis.

tl; dr: En iyi sonuçları elde etmek için, PHP rasgele sayı üretecini tohumlamayın ve iyiliğin iyiliği uniqidiçin kullanıcılara maruz bırakmayın . Bunlardan birini veya ikisini birden yapmak, rastgele sayılarınızın daha tahmin edilebilir olmasına neden olabilir.


PHP 7 Güncellemesi:

PHP 7.0 tanıtır random_bytesve random_inttemel işlevleri olarak. Temeldeki sistemin CSPRNG uygulamasını kullanırlar, bu da onları bir seri rasgele sayı üreticisinin yaşadığı sorunlardan arındırır. openssl_random_pseudo_bytesYalnızca bir uzantıya gerek duymadan etkili bir şekilde benzerler . PHP5 için bir polyfill kullanılabilir .


*: Suhosin güvenlik yaması davranışını değiştirir randve mt_randher çağrı ile böyle her zaman bu yeniden tohum. Suhosin üçüncü bir tarafça sağlanmaktadır. Bazı Linux dağıtımları bunu varsayılan PHP paketlerinde varsayılan olarak içerir, bazıları ise bir seçenek haline getirir ve diğerleri bunu tamamen görmezden gelir.

**: Kullanılan platforma ve temeldeki kütüphane çağrılarına bağlı olarak, burada belgelenenden farklı sekanslar üretilecektir, ancak Suhosin yaması kullanılmadıkça sonuçlar tekrarlanabilir olmalıdır.


Teşekkürler Charles - Cevabınız ve Tangurena'dan gelen lineer eşlenik jeneratör linkini okumak arasında daha iyi bir kavrayışa sahip olduğumu hissediyorum. Rand () yöntemini bu şekilde kullanmanın kötü bir fikir olduğunu zaten biliyordum, ancak nedenini bildiğimi biliyorum .
Erik,

Vay, tam bir hecelenen cevap için sahne, teşekkürler!
David Hobs

10

Fonksiyonun ne kadar rasgele olmadığını görsel olarak göstermek rand()için, işte tüm piksellerin "rastgele" kırmızı, yeşil ve mavi değerlerden oluştuğu bir resim:

Rastgele RGB değerleri

Normalde görüntülerde herhangi bir desen olmamalıdır.

srand()Farklı değerlerle aramayı denedim , bu işlevin ne kadar öngörülebilir olduğunu değiştirmiyor.

Her ikisinin de kriptografik olarak güvenli olmadığını ve öngörülebilir sonuçlar ürettiğini unutmayın.


7

PHP'nin rand () çıktısı PRNG olarak tahmin edilebilir

Bu bir olan lineer kongrüans jeneratör . Araçlarla Yani etkili olan bir işlevi vardır: NEW_NUMBER = (A * OLD_NUMBER + B) MOD C. NEW_NUMBER - OLD_NUMBER numaralı grafiği çizerseniz, çapraz çizgiler görmeye başlarsınız. PHP'nin RAND dokümantasyonundaki notlardan bazıları bunun nasıl yapılacağına örnekler verir.

Bu, şifre kaybettiren kullanıcılar için bir belirteç üretmek için rand () kullanan bir kimlik kütüphanesi gördüğümden ve bunun potansiyel bir güvenlik sorunu olduğunu varsaydım.

Bir windows makinesinde, RAND'ın maksimum değeri 2 ^ 15'tir. Bu, saldırgana kontrol etmesi için yalnızca 32.768 olasılık sunar.

Birisi, birkaç bin tahmin içinde, belirli bir anda rand () aracılığıyla hangi rasgele # üretildiğini makul bir şekilde anlayabilir mi? hatta 10.000 tahmin? Nasıl?

İken bu makale tam aradığınız bir değil, bazı araştırmacılar rastgele sayı üreteci mevcut bir uygulama sürdü ve Texas Holdem para kazanmak için kullandı gösterir. 52 tane var! Muhtemel karıştırılmış desteler, ancak uygulama 32-bit rasgele sayı üreteci kullandı (ki bu bir windows makinesinde mt_getrandmax'ın maks. sayısı) ve gece yarısından itibaren geçen süreyi milisaniye cinsinden tohumladı. Bu, muhtemel karıştırılmış destelerin sayısını yaklaşık 2 ^ 226'dan yaklaşık 2 ^ 27'ye düşürerek gerçek zamanlı olarak arama yapmayı ve hangi güvertenin dağıtıldığını bilmeyi mümkün kılar.

Bunu yaptıktan sonra dışarıya bakacak olursam, jetonun md5 olduğunu bile bilsem bile belirteci nasıl tahmin edeceğimi bilemedim.

Federallerin md5'i bozduğunu düşündüğü için SHA-2 ailesinde bir şeyler kullanmanızı öneririm . Bazı insanlar md5 karma değerlerini çözmek için google kullanıyor, çünkü çok yaygınlar. Sadece karma bir şey sonra google aramaya karma atmak - temelde google dev bir gökkuşağı tablo haline gelmiştir .


1

Rasgele oluşturulmuş bir sayı verildiğinde, bir sonrakinin nispeten tahmin edilebilir olduğunu söylemek gerçekten daha doğrudur. Olabilecek çok fazla sayı var. Ancak bu, tahmin edebileceğiniz anlamına gelmez, daha fazlasını yapan bir program yazabilirsiniz.


1
Bir sonraki sayının tamamen deterministik olduğunu düşünüyorum. "Nispeten" değil, kesinlikle. Sözde rasgele sayı üreteçleriyle ilgili sorun, bir dizinin istatistiksel testleri geçeceği şeklindedir. İki bitişik sayı, tamamen deterministik olsa da, gerçek rasgele sayılarla ortak istatistiksel özelliklere sahip olacaktır.
S.Lott

1
Bir sonraki sayı tamamen belirleyicidir. Sözde rasgele sayı üretecindeki "sözde" nin anlamı budur. Öte yandan, bir sonraki sayının belirlenmesinde ihtiyaç duyulan bilgi pratikte elde edilmesi imkansızdır.
Rein Henrichs

@ S.Lott - 2 ^ 32 olası çıktılarda bir sayının birçok kez görünebileceği ve her göründüğünde farklı bir sayı olabileceği izlenimindeydim. Fakat bir X tohumu verildiğinde, Y'nin sonucunu döndürerek, bir sonraki sonuç her zaman aynı olacaktır. Bu nedenle, pratikte, Y'yi takip eden bir avuç rakam olabilir. PRNG'lere gerçekten bakmayalı çok uzun zaman oldu.
pdr
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.