Kemampuan untuk menebak nilai selanjutnya randterkait dengan kemampuan untuk menentukan apa srandyang dipanggil. Secara khusus, penyemaian sranddengan jumlah yang ditentukan menghasilkan hasil yang dapat diprediksi ! Dari prompt interaktif 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 >
Ini bukan hanya kebetulan. Sebagian besar versi PHP * di sebagian besar platform ** akan menghasilkan urutan 97, 97, 39, 77, 93 saat srandmenggunakan 1024.
Untuk lebih jelasnya, ini bukan masalah dengan PHP, ini adalah masalah dengan implementasi randitu sendiri. Masalah yang sama muncul dalam bahasa lain yang menggunakan implementasi yang sama (atau serupa), termasuk Perl.
Kuncinya adalah bahwa setiap versi PHP waras akan memiliki pra-seeded sranddengan nilai "tidak diketahui". Oh, tapi itu tidak terlalu diketahui. Dari ext/standard/php_rand.h:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Jadi, ini adalah beberapa matematika dengan time(), PID, dan hasil php_combined_lcg, yang didefinisikan dalam ext/standard/lcg.c. Saya tidak akan tinggal di sini, karena mata saya berkaca-kaca dan saya memutuskan untuk berhenti berburu.
Sedikit Googling menunjukkan bahwa area lain dari PHP tidak memiliki properti generasi keacakan terbaik , dan panggilan untuk php_combined_lcgmenonjol di sini, terutama sedikit analisis ini:
Tidak hanya fungsi ini ( gettimeofday) memberikan kami stempel waktu server yang tepat pada plat perak, juga menambahkan output LCG jika kami meminta "lebih banyak entropi" (dari PHP uniqid).
Ya ituuniqid . Tampaknya nilai php_combined_lcgadalah apa yang kita lihat ketika kita melihat digit hex yang dihasilkan setelah memanggil uniqiddengan argumen kedua diatur ke nilai sebenarnya.
Sekarang, dimana kita?
Oh ya. srand.
Jadi, jika kode yang Anda coba prediksi dari nilai acak tidak menelepon srand, Anda harus menentukan nilai yang disediakan oleh php_combined_lcg, yang bisa Anda dapatkan (secara tidak langsung?) Melalui panggilan ke uniqid. Dengan nilai itu di tangan, layak untuk memaksa sisa nilai - time(), PID dan beberapa matematika. Masalah keamanan terkait adalah tentang sesi istirahat, tetapi teknik yang sama akan bekerja di sini. Sekali lagi, dari artikel:
Berikut ringkasan langkah-langkah serangan yang diuraikan di atas:
- tunggu server untuk reboot
- ambil nilai uniqid
- brute memaksa benih RNG dari ini
- polling status online untuk menunggu target muncul
- interleave jajak pendapat status dengan jajak pendapat uniqid untuk melacak waktu server saat ini dan nilai RNG
- ID sesi brute force terhadap server menggunakan interval waktu dan nilai RNG yang ditentukan dalam polling
Cukup ganti langkah terakhir seperti yang diminta.
(Masalah keamanan ini dilaporkan dalam versi PHP yang lebih lama (5.3.2) dari yang kita miliki saat ini (5.3.6), jadi ada kemungkinan bahwa perilaku uniqiddan / atau php_combined_lcgtelah berubah, sehingga teknik khusus ini mungkin tidak bisa diterapkan lagi. YMMV.)
Di sisi lain, jika kode Anda mencoba untuk produk panggilan srandsecara manual , maka kecuali mereka menggunakan sesuatu yang banyak kali lebih baik dari hasil php_combined_lcg, Anda mungkin akan memiliki banyak lebih mudah waktu menebak nilai dan penyemaian lokal Anda generator dengan nomor yang benar. Kebanyakan orang yang secara manual menelepon srandjuga tidak akan menyadari betapa mengerikannya ide ini, dan karenanya tidak mungkin menggunakan nilai yang lebih baik.
Perlu dicatat bahwa mt_randini juga diderita oleh masalah yang sama. Pembibitan mt_sranddengan nilai yang diketahui juga akan menghasilkan hasil yang dapat diprediksi. Mendasarkan entropi Anda openssl_random_pseudo_bytesmungkin adalah taruhan yang lebih aman.
tl; dr: Untuk hasil terbaik, jangan menabur pembuat angka acak PHP, dan demi kebaikan, jangan memaparkan uniqidkepada pengguna. Melakukan salah satu atau keduanya ini dapat menyebabkan angka acak Anda lebih mudah ditebak.
Pembaruan untuk PHP 7:
PHP 7.0 memperkenalkan random_bytesdan random_intsebagai fungsi inti. Mereka menggunakan implementasi sistem yang mendasari CSPRNG, membuat mereka bebas dari masalah yang dimiliki oleh generator nomor acak unggulan. Mereka mirip secara efektif openssl_random_pseudo_bytes, hanya tanpa memerlukan ekstensi untuk diinstal. Polyfill tersedia untuk PHP5 .
*: Patch keamanan Suhosin mengubah perilaku randdan mt_randsedemikian rupa sehingga mereka selalu mengunggah setiap panggilan. Suhosin disediakan oleh pihak ketiga. Beberapa distribusi Linux memasukkannya ke dalam paket PHP resmi mereka secara default, sementara yang lain menjadikannya pilihan, dan yang lain mengabaikannya sama sekali.
**: Bergantung pada platform dan panggilan pustaka yang mendasarinya sedang digunakan, urutan yang berbeda akan dihasilkan dari yang didokumentasikan di sini, tetapi hasilnya masih harus diulang kecuali patch Suhosin digunakan.