La possibilità di indovinare il valore successivo da rand
è legata alla capacità di determinare con che cosa è stato chiamato srand
. In particolare, seeding srand
con un numero predeterminato determina un output prevedibile ! Dal prompt interattivo di 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 >
Questo non è solo un colpo di fortuna. La maggior parte delle versioni PHP * sulla maggior parte delle piattaforme ** genererà la sequenza 97, 97, 39, 77, 93 quando srand
'd con 1024.
Per essere chiari, questo non è un problema con PHP, questo è un problema con l'implementazione di rand
stesso. Lo stesso problema si presenta in altre lingue che utilizzano la stessa implementazione (o simile), incluso Perl.
Il trucco è che qualsiasi versione sana di PHP avrà una% di co_de pre-seeded con un valore "sconosciuto". Oh, ma non è veramente sconosciuto. Da srand
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Quindi, è un po 'di matematica con ext/standard/php_rand.h
, il PID e il risultato di time()
, che è definito in php_combined_lcg
. Non ho intenzione di c & p qui, come, beh, i miei occhi si sono vetrificati e ho deciso di interrompere la caccia.
Un po 'di Google mostra che altre aree di PHP non hanno le migliori proprietà di generazione della casualità , e le chiamate a ext/standard/lcg.c
si distinguono qui, specialmente questo bit di analisi:
Not only does this function (gettimeofday
) hand us back a precise server timestamp on a silver platter, it also adds in LCG output if we request "more entropy" (from PHP's uniqid
).
Sì that php_combined_lcg
. Sembra che il valore di uniqid
sia quello che vediamo quando guardiamo le cifre esadecimali risultanti dopo aver chiamato php_combined_lcg
con il secondo argomento impostato su un valore true.
Ora, dove eravamo?
Oh si. uniqid
.
Quindi, se il codice che stai cercando di prevedere i valori casuali di non chiama srand
, dovrai determinare il valore fornito da srand
, che tu può ottenere (indirettamente?) attraverso una chiamata a php_combined_lcg
. Con quel valore in mano, è fattibile a brute-forzare il resto del valore - uniqid
, il PID e qualche matematica. Il problema legato alla sicurezza riguarda la rottura delle sessioni, ma la stessa tecnica funzionerebbe qui. Di nuovo, dall'articolo:
Here's a summary of the attack steps outlined above:
- wait for the server to reboot
- fetch a uniqid value
- brute force the RNG seed from this
- poll the online status to wait for target to appear
- interleave status polls with uniqid polls to keep track of current server time and RNG value
- brute force session ID against server using the time and RNG value interval established in polling
Sostituisci solo l'ultimo passaggio come richiesto.
(Questo problema di sicurezza è stato segnalato in una versione precedente di PHP (5.3.2) rispetto a quella corrente (5.3.6), quindi è possibile che il comportamento di time()
e / o uniqid
sia cambiato, quindi questo La tecnica specifica potrebbe non essere più utilizzabile. YMMV.)
D'altra parte, se il codice che stai provando per il prodotto chiama php_combined_lcg
manualmente , allora a meno che non stiano usando qualcosa di molte volte migliore del risultato di srand
, tu " Probabilmente avremo un più facile tempo a indovinare il valore e seminare il generatore locale con il numero giusto. La maggior parte delle persone che chiamerebbero manualmente php_combined_lcg
non capirebbe quanto sia orribile un'idea, e quindi non è probabile che utilizzi valori migliori.
Vale la pena notare che srand
è anche afflitto dallo stesso problema. Seminare mt_rand
con un valore noto produrrà anche risultati prevedibili. Basare la tua entropia su mt_srand
è probabilmente una scommessa più sicura.
tl; dr: per ottenere i migliori risultati, non seminare il generatore di numeri casuali PHP e, per l'amor del cielo, non esporre openssl_random_pseudo_bytes
agli utenti. L'esecuzione di uno o entrambi può causare l'indecisione dei numeri casuali.
Aggiornamento per PHP 7:
PHP 7.0 introduce uniqid
e random_bytes
come funzioni principali. Usano l'implementazione CSPRNG del sistema sottostante, rendendoli liberi dai problemi che ha un generatore di numeri casuali seminato. Sono effettivamente simili a random_int
, solo senza bisogno di un'estensione da installare. Un polyfill è disponibile per PHP5 .
*: la patch di sicurezza Suhosin modifica il comportamento di openssl_random_pseudo_bytes
e rand
in modo tale che -seed con ogni chiamata. Suhosin è fornito da una terza parte. Alcune distribuzioni Linux lo includono nei loro pacchetti ufficiali di PHP per impostazione predefinita, mentre altri lo rendono un'opzione, mentre altri lo ignorano del tutto.
**: A seconda della piattaforma e delle chiamate della libreria sottostante utilizzate, verranno generate sequenze diverse rispetto a quelle documentate qui, ma i risultati dovrebbero essere ancora ripetibili a meno che non venga utilizzata la patch Suhosin.