Encriptando senhas de forma segura

encriptando-senhas-forma-segura-topo

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
view raw md5.php This Gist brought to you by GitHub.

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
view raw md5-salt.php This Gist brought to you by GitHub.

Dessa forma, todas as senhas estarão mais protegidas… porém ainda temos um problema:

O problema: salt fixo

  1. Todas as senhas usam o mesmo salt
  2. O salt (que é fixo) está presente em algum arquivo/texto dentro do seu sistema
  3. O invasor que conseguiu pegar o seu banco de dados (de senhas) também vai ter acesso aos arquivos e, consequentemente, ao salt
  4. 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.

Fonte: Thiago Belém

Postagens Relacionadas