Quero gerar um identificador para esqueci a senha. Eu li que posso fazer isso usando timestamp com mt_rand (), mas algumas pessoas estão dizendo que o timestamp pode não ser exclusivo todas as vezes. Estou um pouco confuso aqui. Posso fazer isso usando carimbo de hora com isso?
Pergunta
Qual é a prática recomendada para gerar tokens aleatórios / exclusivos de comprimento personalizado?
Eu sei que há muitas perguntas feitas por aqui, mas estou ficando mais confuso depois de ler opiniões diferentes de pessoas diferentes.
Respostas:
Em PHP, use
random_bytes()
. Motivo: você está procurando uma maneira de obter um token de lembrete de senha e, se for uma credencial de login única, então você realmente tem dados para proteger (que é - conta de usuário inteira)Portanto, o código será o seguinte:
Atualização : referia-se a versões anteriores desta resposta
uniqid()
e isso é incorreto se for uma questão de segurança e não apenas de exclusividade.uniqid()
é essencialmente apenasmicrotime()
com alguma codificação. Existem maneiras simples de obter previsões precisas domicrotime()
em seu servidor. Um invasor pode emitir uma solicitação de redefinição de senha e, em seguida, tentar alguns tokens prováveis. Isso também é possível se more_entropy for usado, pois a entropia adicional é igualmente fraca. Obrigado a @NikiC e @ScottArciszewski por apontar isso.Para mais detalhes veja
fonte
random_bytes()
só está disponível a partir do PHP7. Para versões mais antigas, a resposta por @yesitsme parece ser a melhor opção.$length
? O id do usuário? Ou o que?Isso responde à solicitação de 'melhor aleatório':
A resposta 1 de Adi de Security.StackExchange tem uma solução para isso:
1. Adi, seg, 12 de novembro de 2018, Celeritas, "Generating an unguessable token for confirm e-mails", set 20 '13 at 7:06, https://security.stackexchange.com/a/40314/
fonte
openssl_random_pseudo_bytes($length)
- suporte: PHP 5> = 5.3.0, ....................................... ................... (Para PHP 7 e superior, userandom_bytes($length)
) ...................... .................... (Para PHP abaixo de 5.3 - não use PHP abaixo de 5.3)A versão anterior da resposta aceita (
md5(uniqid(mt_rand(), true))
) é insegura e oferece apenas cerca de 2 ^ 60 resultados possíveis - bem dentro do intervalo de uma busca de força bruta em cerca de uma semana para um atacante de baixo orçamento:mt_rand()
é previsível (e só adiciona 31 bits de entropia)uniqid()
adiciona apenas 29 bits de entropiamd5()
não adiciona entropia, apenas a mistura deterministicamenteComo uma chave DES de 56 bits pode sofrer força bruta em cerca de 24 horas , e um caso médio teria cerca de 59 bits de entropia, podemos calcular 2 ^ 59/2 ^ 56 = cerca de 8 dias. Dependendo de como essa verificação de token é implementada, pode ser possível vazar praticamente informações de tempo e inferir os primeiros N bytes de um token de redefinição válido .
Já que a pergunta é sobre "melhores práticas" e começa com ...
... podemos inferir que este token tem requisitos de segurança implícitos. E quando você adiciona requisitos de segurança a um gerador de números aleatórios, a prática recomendada é sempre usar um gerador de números pseudo-aleatórios criptograficamente seguro (abreviado CSPRNG).
Usando um CSPRNG
No PHP 7, você pode usar
bin2hex(random_bytes($n))
(onde$n
é um número inteiro maior que 15).No PHP 5, você pode usar
random_compat
para expor a mesma API.Alternativamente,
bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
se vocêext/mcrypt
instalou. Outra boa linha ébin2hex(openssl_random_pseudo_bytes($n))
.Separando a pesquisa do validador
Puxando do meu trabalho anterior sobre cookies "lembrar-me" seguros em PHP , a única maneira eficaz de mitigar o vazamento de tempo mencionado anteriormente (normalmente introduzido pela consulta de banco de dados) é separar a pesquisa da validação.
Se sua tabela for assim (MySQL) ...
... você precisa adicionar mais uma coluna,
selector
assim:Use um CSPRNG Quando um token de redefinição de senha é emitido, envie ambos os valores para o usuário, armazene o seletor e um hash SHA-256 do token aleatório no banco de dados. Use o seletor para obter o hash e a ID do usuário, calcule o hash SHA-256 do token que o usuário fornece com aquele armazenado no banco de dados usando
hash_equals()
.Código de exemplo
Gerando um token de redefinição no PHP 7 (ou 5.6 com random_compat) com PDO:
Verificando o token de redefinição fornecido pelo usuário:
Esses fragmentos de código não são soluções completas (evitei a validação de entrada e as integrações de estrutura), mas devem servir como um exemplo do que fazer.
fonte
hash('sha256', bin2hex($token))
, 2) verificar comif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Obrigado!id
como seletor? Quer dizer, a chave primária daaccount_recovery
tabela. Não precisamos de camada adicional de segurança para o seletor, precisamos? Obrigado!id:secret
está bem.selector:secret
está bem.secret
em si não é. O objetivo é separar a consulta do banco de dados (que tem perda de tempo) do protocolo de autenticação (que deve ser de tempo constante).openssl_random_pseudo_bytes
em vezrandom_bytes
se está rodando o PHP 5.6? Além disso, você não deveria anexar apenas o seletor e não o validador na string de consulta do link?Você também pode usar DEV_RANDOM, onde 128 = 1/2 do comprimento do token gerado. O código abaixo gera 256 token.
fonte
MCRYPT_DEV_URANDOM
sobreMCRYPT_DEV_RANDOM
.Isso pode ser útil sempre que você precisar de um token muito aleatório
fonte