Kemampuan untuk menebak nilai selanjutnya rand
terkait dengan kemampuan untuk menentukan apa srand
yang dipanggil. Secara khusus, penyemaian srand
dengan 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 srand
menggunakan 1024.
Untuk lebih jelasnya, ini bukan masalah dengan PHP, ini adalah masalah dengan implementasi rand
itu 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 srand
dengan 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_lcg
menonjol 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_lcg
adalah apa yang kita lihat ketika kita melihat digit hex yang dihasilkan setelah memanggil uniqid
dengan 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 uniqid
dan / atau php_combined_lcg
telah berubah, sehingga teknik khusus ini mungkin tidak bisa diterapkan lagi. YMMV.)
Di sisi lain, jika kode Anda mencoba untuk produk panggilan srand
secara 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 srand
juga tidak akan menyadari betapa mengerikannya ide ini, dan karenanya tidak mungkin menggunakan nilai yang lebih baik.
Perlu dicatat bahwa mt_rand
ini juga diderita oleh masalah yang sama. Pembibitan mt_srand
dengan nilai yang diketahui juga akan menghasilkan hasil yang dapat diprediksi. Mendasarkan entropi Anda openssl_random_pseudo_bytes
mungkin adalah taruhan yang lebih aman.
tl; dr: Untuk hasil terbaik, jangan menabur pembuat angka acak PHP, dan demi kebaikan, jangan memaparkan uniqid
kepada pengguna. Melakukan salah satu atau keduanya ini dapat menyebabkan angka acak Anda lebih mudah ditebak.
Pembaruan untuk PHP 7:
PHP 7.0 memperkenalkan random_bytes
dan random_int
sebagai 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 rand
dan mt_rand
sedemikian 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.