A capacidade de adivinhar o próximo valor rand
está ligada à capacidade de determinar o que srand
foi chamado. Em particular, a propagação srand
com um número predeterminado resulta em resultados previsíveis ! No prompt interativo do 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 >
Isso não é apenas um acaso. A maioria das versões do PHP * na maioria das plataformas ** gerará a sequência 97, 97, 39, 77, 93 quando srand
usada com 1024.
Para ser claro, isso não é um problema com o PHP, é um problema com a implementação em rand
si. O mesmo problema aparece em outros idiomas que usam a mesma implementação (ou similar), incluindo Perl.
O truque é que qualquer versão sã do PHP terá pré-propagada srand
com um valor "desconhecido". Ah, mas não é realmente desconhecido. De ext/standard/php_rand.h
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Então, é um pouco de matemática com time()
, o PID e o resultado de php_combined_lcg
, que é definido em ext/standard/lcg.c
. Eu não vou c & p aqui, bem, meus olhos vidraram e eu decidi parar de caçar.
Um pouco de pesquisa no Google mostra que outras áreas do PHP não têm as melhores propriedades de geração de aleatoriedade , e chamadas para php_combined_lcg
se destacar aqui, especialmente esta parte da análise:
Essa função ( gettimeofday
) não apenas nos devolve um carimbo de data / hora preciso do servidor em uma bandeja de prata, mas também adiciona a saída LCG se solicitarmos "mais entropia" (do PHP uniqid
).
Sim issouniqid
. Parece que o valor de php_combined_lcg
é o que vemos quando olhamos para os dígitos hexadecimais resultantes depois de chamar uniqid
com o segundo argumento definido como um valor verdadeiro.
Agora onde estávamos?
Ai sim. srand
.
Portanto, se o código do qual você está tentando prever valores aleatórios não for chamado srand
, será necessário determinar o valor fornecido pelo php_combined_lcg
qual você pode obter (indiretamente?) Através de uma chamada para uniqid
. Com esse valor em mãos, é possível forçar o restante do valor - time()
, o PID e algumas contas. O problema de segurança vinculado é sobre interromper as sessões, mas a mesma técnica funcionaria aqui. Novamente, a partir do artigo:
Aqui está um resumo das etapas de ataque descritas acima:
- aguarde o servidor reiniciar
- buscar um valor uniqid
- força bruta a semente RNG deste
- sondar o status online para aguardar o destino aparecer
- intercalar pesquisas de status com pesquisas uniqid para acompanhar o tempo atual do servidor e o valor RNG
- ID da sessão de força bruta no servidor usando o intervalo de tempo e valor RNG estabelecido na pesquisa
Apenas substitua a última etapa, conforme necessário.
(Esse problema de segurança foi relatado em uma versão anterior do PHP (5.3.2) do que a atual (5.3.6); portanto, é possível que o comportamento de uniqid
e / ou php_combined_lcg
tenha sido alterado, portanto, essa técnica específica pode não ser mais viável. YMMV.)
Por outro lado, se o código para o qual você está tentando produto ligar srand
manualmente , a menos que eles estejam usando algo muitas vezes melhor que o resultado php_combined_lcg
, provavelmente será mais fácil adivinhar o valor e semear seu local gerador com o número certo. A maioria das pessoas que telefonaria manualmente srand
também não perceberia o quão horrível é uma ideia e, portanto, provavelmente não usará valores melhores.
Vale ressaltar que mt_rand
também é afetado pelo mesmo problema. A propagação mt_srand
com um valor conhecido também produzirá resultados previsíveis. Basear sua entropia openssl_random_pseudo_bytes
é provavelmente uma aposta mais segura.
tl; dr: Para obter melhores resultados, não propague o gerador de números aleatórios PHP e, pelo amor de Deus, não exponha uniqid
aos usuários. Se você fizer um ou os dois, poderá tornar seus números aleatórios mais fáceis de adivinhar.
Atualização para o PHP 7:
O PHP 7.0 apresenta random_bytes
e random_int
como principais funções. Eles usam a implementação CSPRNG do sistema subjacente, liberando-os dos problemas que um gerador de números aleatórios semeado tem. Eles são efetivamente semelhantes openssl_random_pseudo_bytes
, apenas sem a necessidade de instalar uma extensão. Um polyfill está disponível para PHP5 .
*: O patch de segurança Suhosin altera o comportamento de rand
e de mt_rand
modo que eles sempre sejam reproduzidos novamente a cada chamada. Suhosin é fornecido por terceiros. Algumas distribuições Linux o incluem em seus pacotes oficiais do PHP por padrão, enquanto outras o tornam uma opção e outras o ignoram completamente.
**: Dependendo da plataforma e das chamadas de biblioteca subjacentes que estão sendo usadas, serão geradas sequências diferentes das documentadas aqui, mas os resultados ainda deverão ser repetidos, a menos que o patch Suhosin seja usado.