La capacité de deviner la valeur suivante rand
est liée à la capacité de déterminer ce qui a srand
été appelé. En particulier, l' ensemencement srand
avec un nombre prédéterminé entraîne une sortie prévisible ! Depuis l'invite interactive 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 >
Ce n'est pas seulement un coup de chance. La plupart des versions PHP * sur la plupart des plates-formes ** généreront la séquence 97, 97, 39, 77, 93 srand
avec 1024.
Pour être clair, ce n'est pas un problème avec PHP, c'est un problème avec l'implémentation de rand
lui - même. Le même problème apparaît dans d'autres langues qui utilisent la même implémentation (ou une implémentation similaire), y compris Perl.
L'astuce est que toute version saine de PHP aura pré-ensemencé srand
avec une valeur "inconnue". Oh, mais ce n'est pas vraiment inconnu. De ext/standard/php_rand.h
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Donc, c'est un peu de calcul avec time()
, le PID et le résultat de php_combined_lcg
, qui est défini dans ext/standard/lcg.c
. Je ne vais pas c & p ici, car, bien, mes yeux brillaient et j'ai décidé d'arrêter de chasser.
Un peu de recherche sur Google montre que d' autres domaines de PHP n'ont pas les meilleures propriétés de génération d'aléatoire , et appelle à php_combined_lcg
se démarquer ici, en particulier ce morceau d'analyse:
Non seulement cette fonction ( gettimeofday
) nous rend un horodatage précis du serveur sur un plateau d'argent, mais elle ajoute également une sortie LCG si nous demandons "plus d'entropie" (de PHP uniqid
).
Ouais çauniqid
. Il semble que la valeur de php_combined_lcg
soit ce que nous voyons lorsque nous regardons les chiffres hexadécimaux résultants après l'appel uniqid
avec le deuxième argument défini sur une valeur vraie.
Maintenant, où en étions-nous?
Oh oui. srand
.
Donc, si le code à partir duquel vous essayez de prédire des valeurs aléatoires n'appelle passrand
, vous devrez déterminer la valeur fournie par php_combined_lcg
, que vous pouvez obtenir (indirectement?) Via un appel à uniqid
. Avec cette valeur en main, il est possible de forcer brutalement le reste de la valeur - time()
, le PID et quelques calculs. Le problème de sécurité lié concerne la rupture des sessions, mais la même technique fonctionnerait ici. Encore une fois, à partir de l'article:
Voici un résumé des étapes d'attaque décrites ci-dessus:
- attendez que le serveur redémarre
- récupérer une valeur uniqid
- force brute la graine RNG de cette
- interroger l'état en ligne pour attendre que la cible apparaisse
- entrelacer les sondages d'état avec les sondages uniqid pour garder une trace de l'heure actuelle du serveur et de la valeur RNG
- ID de session de force brute sur le serveur à l'aide de l'intervalle de temps et de valeur RNG établi lors de l'interrogation
Remplacez simplement cette dernière étape si nécessaire.
(Ce problème de sécurité a été signalé dans une version PHP antérieure (5.3.2) que nous avons actuellement (5.3.6), il est donc possible que le comportement de uniqid
et / ou php_combined_lcg
ait changé, donc cette technique spécifique pourrait ne plus fonctionner. YMMV.)
D'un autre côté, si le code que vous essayez de produire du produit appelle srand
manuellement , à moins qu'ils n'utilisent quelque chose de bien meilleur que le résultat php_combined_lcg
, vous aurez probablement beaucoup plus de facilité à deviner la valeur et à amorcer votre local générateur avec le bon numéro. La plupart des gens qui appelleraient manuellement srand
ne réaliseraient pas non plus à quel point cette idée est horrible, et ne sont donc pas susceptibles d'utiliser de meilleures valeurs.
Il convient de noter qu'il mt_rand
est également touché par le même problème. L'ensemencement mt_srand
avec une valeur connue produira également des résultats prévisibles. Baser votre entropie sur openssl_random_pseudo_bytes
est probablement un pari plus sûr.
tl; dr: pour de meilleurs résultats, n'amorcez pas le générateur de nombres aléatoires PHP et, pour l'amour du ciel, ne l'exposez pas uniqid
aux utilisateurs. Si vous effectuez l'une de ces actions ou les deux, vous risquez de deviner vos nombres aléatoires.
Mise à jour pour PHP 7:
PHP 7.0 présente random_bytes
et en random_int
tant que fonctions principales. Ils utilisent l'implémentation CSPRNG du système sous-jacent, ce qui les libère des problèmes rencontrés par un générateur de nombres aléatoires. Ils sont effectivement similaires à openssl_random_pseudo_bytes
, uniquement sans avoir besoin d'installer une extension. Un polyfill est disponible pour PHP5 .
*: Le correctif de sécurité Suhosin modifie le comportement de rand
et de mt_rand
telle sorte qu'ils se ré- amorcent toujours à chaque appel. Suhosin est fourni par un tiers. Certaines distributions Linux l'incluent par défaut dans leurs packages PHP officiels, tandis que d'autres en font une option, et d'autres l'ignorent complètement.
**: En fonction de la plate-forme et des appels de bibliothèque sous-jacents utilisés, des séquences différentes seront générées que celles décrites ici, mais les résultats devraient toujours être reproductibles à moins que le patch Suhosin soit utilisé.