Do MD5/SHA-1 para o futuro

Se o SHA-1 já é considerado não seguro o MD5 eu não preciso nem comentar sobre ele…

Para quem não viu, em fevereiro de 2017, o Google junto com o instituto CWI confirmou a colisão de SHA-1 e fez a publicação da matéria explicando os processos, linkse aqui.

Olhando para esse cenário, é bem interessante (leia-se: melhor fazer) mudar a forma como você esta gerando os hashs de senhas de seus usuários.

Vamos ao cenário proposto. Você tem uma base de usuários que esta toda usando MD5 (ou SHA-1) e não é interessante você enviar um e-mail para todos eles dizendo que eles precisam alterar suas senhas, com isso, você precisa atualizar o hash de senhas de forma transparente.

A forma como o PHP trata as senhas é com as funções “password_” (você pode ler sobre elas Como armazenar as senhas corretamente com PHP e COST do password_hash), estas funções permitem a nós trabalhar com as senhas da forma mais segura possível para o PHP e além disso, sua implimentação correta nos possibilita estar sempre atualizados nas questões de hashs mais seguros.

Como resolver o cenário de uma base com hashs antigos.

A forma como você faz login hoje é mais ou menos dessa forma:

Como o MD5 gera sempre o mesmo hash para o mesmo dado que foi passado, muitos utilizam o MD5 na consulta do MySQL para trazer o usuário que já tem aquele e-mail e senha, se você esta fazendo isso vai precisar alterar para essa forma, pois na hora de verificar a senha precisamos passar a senha que o usuário informou e o hash para a função “password_verify”.

Entendendo como as funções “password_” operam, devemos alterar o código acima para este:

Esse código basicamente faz a mesma validação de MD5, porém quando o usuário é válido para o MD5 ele já faz um novo hash de senha utilizando a “password_hash” e quando não passa no MD5 ainda não quer dizer que falhou o login, ele vai verificar se o hash é o novo e dessa forma validar com “password_verify”, além disso, você pode ver a função “password_needs_rehash”, ela é utilizada para verificar se o hash ainda é válido para as configurações, imagine que nós melhoramos nossa infraestrutura e com isso podemos mudar o valor da opção “cost”, com isso um hash gerado com “cost 10” não é mais válido para um “cost 11” e por conta disso devemos refazer o hash assim melhorando a segurança dele. Isso também é válido para quando mudamos o algorítimo de hash.

Como você pode ver no comentário da validação MD5, esta marcado para remover essa condição, isso porque quando você não tiver mais senhas em MD5 essa condição será inútil.

Agora a última parte, digamos que você deixe esse código por 1 ano em seu sistema e mesmo assim você ainda tem senhas MD5 em sua base de dados, como resolver isso, já que você não quer deixar seus usuários expostos, mesmo aqueles que não utilizam mais seu sistema.

Como me disse o @ericktedeschi. “Deixa assim por um ano e depois faz o reset da senha, se o cara esta a um ano sem entrar no seu sistema ele nem deve lembrar a senha”. É isso, passou muito tempo, de um reset na senha daqueles usuários. Dessa forma você não vai ter a senha deles em MD5.

Isso é tudo. Fazendo a implementação correta das funções “password_”, você esta protegendo seus usuários e ainda fazendo com que novos algoritmos de hash sejam implementados sem alterações estruturais de seu código.

COST do password_hash

Como complemento para o post Como armazenar as senhas corretamente com PHP, quero mostrar como o parâmetro “cost” é utilizado no password_hash e com isso te ajudar a entender qual é o valor mais adequado para você usar.

Um detalhe quanto ao valor do “cost”, não há um valor certo para a sua aplicação, você tem que escolher um valor que faça sentido para ela. Por isso esse post, ele é para explicar justamente o impacto que esse parâmetro tem.

Para saber o tempo que de cada valor do “cost” executei esse script.

E esse foi o resultado em segundos.

A configuração da máquina que estou usando é essa.

Com base nesses valores, você consegue ter uma base de quanto tempo vai levar para gerar uma senha, lembrando que sempre que um usuário se cadastrar, ou logar ela vai chamar os comandos de password várias vezes e isso vai impactar no tempo que ele leva para entrar em seu sistema e fora isso, vai sobrecarregar o seu servidor, então, outros usuários podem ser impactados com isso.

É isso.

Como armazenar as senhas corretamente com PHP

Garantir que as senhas dos usuários estão armazenadas corretamente no banco de dados da um certo trabalho, mas assim, ainda não é ciência de foguete, para você usar md5 porque é fácil. Vale lembrar que o md5 não serve para nada e bem no nível do NADA MESMO.

Muitas vezes, a validação de login fica como responsabilidade do banco de dados (Oi? Claro que não, ta louco!).

Quem não fez algo assim (favor ignorar o sql injection):

$sql = “SELECT id FROM user WHERE email=’$email’ AND password=MD5(‘$password’)”;

Você não valida depois se veio uma linha? Então, quem validou foi o banco…

Sendo desse jeito, não temos muito o que fazer. A primeira coisa que precisa ocorrer é alterar quem valida a senha do usuário e isso quem deve fazer é a aplicação.

Nesse contexto, vamos usar as funções de senhas do PHP.

password_hash

password_verify

Conforme manual, password_hash, vai gerar uma senha bem diferente do md5 e se fizer certinho não mexe mais (spoiler, explico no final).

Simples nesse nível:

Agora, o que é o que.

PASSWORD_DEFAULT

É uma constante do PHP que justamente gerencia qual será o algoritimo de criptografia. Ela será alterada caso em uma nova versão do PHP tenha uma forma melhor de criptografia.

cost

É o “custo” daquele hash, quanto maior o custo maior é o tempo para gerar uma senha (explicação prática do cost aqui). Por padrão o cost é 10, logo se eu não passase ele daria na mesma.

Blz, já salvei a senha do meu usuário e tudo mais, agora como eu valido isso?

Pois é, “tudo” isso.

Como bônus, agora vem a parte do corretamente.

O “password_needs_rehash” verifica se o hash usado como senha ainda é o melhor que há disponível.

Pense assim, hoje você esta usando o “cost” 10, mas amanhã vai começar a usar o 11, ou 12, ou o “PASSWORD_DEFAULT” mudou, quando que você vai alterar o hash da senha do usuário? Dessa forma, você já aproveita que você tem a senha do usuário e criptografa ela no novo padrão de hash.

Dessa forma, quando mudar o padrão de criptografia, ou quando você mudar a configuração do “cost” seu código já vai estar preparado para isso e o hash vai ser alterado para o mais novo.

Espero que isso ajude 🙂