Desenvolvimento - PHP
Encriptando senhas de forma segura
Veja neste artigo como encriptar suas senhas de uma forma segura e que dificulte a ação de hackers roubarem seus dados.
por Thiago BelemFala pessoal, tudo bom?
Hoje vamos falar um pouquinho sobre “Segurança”, e uma das coisas que acho mais interessante, nessa área, é a encriptação de senhas.
Esses dias li um artigo muito bom (bem antigo por sinal) no NetTuts+ e achei legal trazer algumas informações pra cá, de forma bem resumida e direta, porém recomendo muito a leitura do artigo original.
Hashing
Hashing consiste em proteger dados (strings, números), convertendo-os em um novo dado, geralmente menor e em formato de string ou inteiro.
Hashes geralmente são mão-única, o que significa que não há uma forma de reverter a encriptação, ou encontrar o dado original baseado no hash (resultado da encriptação).
O problema
Estamos acostumados a usar hashes como MD5 e SHA1 da seguinte forma:
<?php // Senha do usuário, pode ter vindo do $_POST, $_GET ou outro lugar $senha = 'olá mundo'; // Encripta a senha usando MD5 $senha = md5($senha); // Resultado: // ca4e913424bfcfe71c016829a371a1f1 // Salvamos essa senha encriptada no banco
No caso do MD5, resultado final é sempre uma string de 32 caracteres alfa-numéricos (128 bits).
Você pode usar o MD5 e pensar que está seguro, mas existe uma coisa chamada Rainbow Tables, onde um atacante gera uma tabela com o resultado da encriptação de todas as palavras de um dicionário, combinando palavras e até adicionando símbolos e dígitos à essas palavras…. Com essa Rainbow Table fica muito fácil (partindo do resultado final da encriptação) descobrir a senha original (olá mundo).
A solução simples: salts
A solução mais simples é utilizar um “salt” que é uma string complexa que será concatenada a toda e qualquer senha antes de encriptá-la, por exemplo:
<?php $salt = '1%1cAu!g+>K53PY}'; // Senha do usuário, pode ter vindo do $_POST, $_GET ou outro lugar $senha = 'olá mundo'; // Encripta a senha usando MD5 $senha = md5($senha . $salt); // Resultado: // c1de0ebde1fd59955ccd57ccd89ac2e9 // Salvamos essa senha encriptada no banco
Dessa forma, todas as senhas estarão mais protegidas… porém ainda temos um problema:
O problema: salt fixo
- Todas as senhas usam o mesmo salt
- O salt (que é fixo) está presente em algum arquivo/texto dentro do seu sistema
- O invasor que conseguiu pegar o seu banco de dados (de senhas) também vai ter acesso aos arquivos e, consequentemente, ao salt
- Com posse do salt o atacante gera uma Rainbow Table nova, usando aquele salt nas combinações.
Precisamos então - de alguma forma - proteger o salt, ou gerar um salt novo pra cada senha, o que seria o ideal.
A solução complicada: salts dinâmicos
Podemos gerar uma string aleatória no PHP de várias formas, mas a idéia principal aqui é: gerar uma string aleatória, utilizá-la como salt na hora de encriptar a senha do usuário e salvar AMBAS no banco de dados (a senha e a string utilizada como salt).
<?php /** * Gera um salt aleatório * * @param int $tamanho Tamanho do salt * * @return string */ function geraSaltAleatorio($tamanho = 22) { return substr(sha1(mt_rand()), 0, $tamanho); } $salt = geraSaltAleatorio(); // Senha do usuário, pode ter vindo do $_POST, $_GET ou outro lugar $senha = 'olá mundo'; // Encripta a senha usando MD5 $senha = md5($senha . $salt); // Resultado: // c1de0ebde1fd59955ccd57ccd89ac2e9 // Salvamos $senha e $salt no banco de dados
Dessa forma, cada senha terá seu próprio salt e o atacante teria que gerar uma rainbow table pra cada salt, o que fica impraticável.
Mas infelizmente ainda temos um problema…
O problema: tempo
A maioria dos métodos de encriptação que conhecemos (como MD5 e SHA1) são criados para serem extremamente rápidos, pois são utilizados na verificação de integridade de arquivos… o que acaba sendo um tiro no pé quando estamos falando de segurança: quanto mais rápido o algoritmo mais fácil um ataque de força-bruta (com ou sem Rainbow Tables) pode conseguir encontrar a senha original.
Precisamos então trocar de algoritmo ou atrasar o nosso script…
A solução: atrasando o algoritmo
<?php /** * Gera um salt aleatório * * @param int $tamanho Tamanho do salt * * @return string */ function geraSaltAleatorio($tamanho = 22) { return substr(sha1(mt_rand()), 0, $tamanho); } $salt = geraSaltAleatorio(); // Senha do usuário, pode ter vindo do $_POST, $_GET ou outro lugar $senha = 'olá mundo'; // Cria um hash $hash = md5($senha . $salt); // Encripta esse hash 1000 vezes for ($i = 0; $i < 1000; $i++) { $hash = md5($hash); } // Salvamos $hash e $salt no banco de dados
Agora qualquer ataque de força-bruta irá demorar 1000x mais para conseguir chegar até sua senha original, o que é excelente!
Finalizando…
O artigo original não termina por aqui, ele sugere a utilização de um algoritmo chamado BLOWFISH que recebe um parâmetro onde você determina o “custo”, que está ligado à demora/ciclos de encriptação… quanto maior, mais demorado.
Espero que tenham entendido a idéia geral e tenham gostado!
Artigo originalmente publicado por Thiago Belem: Encriptando senhas de forma segura