Chave e fechadura

Encriptando senhas de forma segura

Fala pessoal, tudo bom?

Volta e meia gosto de vir aqui falar um pouquinho sobre “Segurança” e uma das coisas que acho mais interessante – nessa área – e a encriptação de senhas.

Hoje 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:

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:

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).

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

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! :)

28 ideias sobre “Encriptando senhas de forma segura

  1. Erick

    Excelente artigo!!! Esse Thiago sim é um profissional que sabe lhe dar com segurança, ao contrário do que vemos muito em empresas e sites brasileiros, que, se bobear, usam até Base64 para criptografar nossas senhas.
    Parabéns, Thiago!

  2. Tiago

    Olá, minha pergunta é a seguinte, lendo seu posto sobre como dificultar a vida de quem invadiu um banco de dados, me fica a pergunta? conseguem facilmente invadir banco de dados? como prevenir a invasão do banco propriamente dito.
    obs: post legal, parabéns por compartilhar o conhecimento

  3. ThiagoLimah

    Desculpe, não tive tempo de ler todas as replys, mas se usar um salt aleatório para criar um hash da senha do usuário, como poderei chegar ao mesmo hash para logar este usuário no sistema se esta senha for utilizada para um login por exemplo? Eu teria que salvar o salt junto ao cadastro na base de dados?

    Vlw

  4. Douglas

    Bom dia Thiago, sou novo no PHP e desculpe por desenterrar o post… No caso você cria um Salt aleatório + a senha ai codifica certo… até ai tudo ok! Bem se o Salt é aleatório… como verificar a senha com a senha digitada pelo usuário? Se armazenar o Salt o invasor irá descobrir e ai começa o ataque ‘Rainbow Table’… como ficaria?

    Abraços e ótimo blog!! Estou acompanhando!!

    1. Ancelmo

      Como o Thiago ja falou, desse modo o atacante terá que tentar uma rainbow table para cada usuário.

      Podemos enganar o atacante criando algoritmos que não usamos e embaralhando tudo e confundindo no codigo fonte e na base de dados para dificultar ao máximo o ataque. Parece bobo mas funciona.

      Não tem como ser 100% seguro, mas a idéia é essa, dificultar ao máximo um ataque de modo que ele desista, ou demore tempo suficiente para ser pego.

    2. Davidson

      Oi Douglas, realmente tem de ter armazenado o tal salt aleatório, mas isso não precisa necessariamente estar explícito, como por exemplo, o salt poderia ser a junção das strings: nome,email,idade e o invasor não teria conhecimento disto gerando hashes diferentes para senhas iguais de usuários diferentes, por ex; mas o conhecimento do salt seja ele aleatório ou não é realmente necessário.

  5. Eric Ruiz

    Bom o artigo.

    Derivado do AES o Whirlpool é excelente (daqui um tempo, vai saber rs), mas na hora de implementar vale mesmo a criatividade, mesclar várias técnicas com salt(s) randomico(s) e SEMPRE desconfiar dos dados que trafegam entre cliente e servidor.

    Em aplicações web eu procuro validar tudo: browser, SO, IP, cookie… e umas coisinhas a mais (hehe) além de apenas login e senha.

    Dependendo da aplicação, segurança nunca é exagero ;)

    ** Não esqueçam de trafegar (ao menos) o login e senha em conexões https criptografadas com SSL3 se possível =) ;)

  6. Gabriel

    Afff… que complexo mesmo hein!
    Gostei… mt bom, já tinha lido o artigo original, mas não tinha entendido direito, vlw por simplificar!

  7. Helder Lourenço

    Eu uso o Whirlpool em vez de MD5 ou SHA1 sem qualquer salt, que gera uma senha de 512bits contra os 128 do MD5. A dúvida é saber se já existem rainbow tables para Whirlpool ? Ou se já alguém conseguir quebrar uma senha Whirlpool ? Alguém tem conhecimento ? Obrigado desde já.

    1. Davidson

      Opa, sobre existir raibow tables para Whirlpool não saberia te dizer se existe ou não (mas usar salt nunca é demais,:D), agora com relação a quebra das senhas, acho um pouco difícil já que é 512bits e mão única; a unica forma que acho (pouquíssimo) possível é por colisão de hashes e mesmo assim isso levaria um bom tempo tornando inviável. E fica tranquilo que o Whirlpool já consegue dar conta do recado.

  8. Abelardo

    O melhor a ser feito é encripitar a string com SHA1 que vai gerar um hash muito maior que o md5 e em seguida encripitar este hash que foi gerado pelo SHA1 para md5, ou seja, gera um hash menor com algo extremamente grande tornando praticamente impossivel de descobrir.

  9. Rodrigo

    Pergunta boba: Se o cara tem acesso ao banco de dados completo e também tem acesso para ver todos os arquivos do seu servidor o que mas ele poderia querer? Ele já não está com a faca e o queijo na mão?

  10. Igor Santos

    Desculpe se estou falando merda, mas qual é a diferença pra segurança do salt se salvarmos a string num arquivo do sistema (no caso do salt único) ou no banco de dados?
    Se o invasor teve acesso ao meu banco / sistema de arquivos, ele vai poder ler os salts do banco da mesma forma, não?
    A única vantagem que vejo aí é a demora que o atacante terá para conseguir desvendar todas as senhas.

    1. Thiago Belem Autor do post

      @Igor e @Tiago,

      Sim, a principio não faz sentido você ter o salt salvo no banco de dados, mas com um salt diferente pra cada usuário o invasor vai ter que gerar uma rainbow table por usuário, o que fica impraticável se você ainda encriptar o hash 1000x.

      A questão aqui não é proteger 1 usuário, e sim a base como um todo.

      Não existe saída 100% segura contra um invasor que tenha todos os arquivos e o banco de dados em mãos, qualquer lógica vai estar dentro de um desses dois locais… o que podemos fazer é dificultar, gerando salts únicos e atrasando o algoritmo.

  11. thiago

    Bom trabalho!, mas tenho uma dúvida:
    Seguinte, se faço esse hash usando salt dinamico, como faço para realizar um login por exemplo?
    pq a pessoa entraria com o login e senha e eu teria que comparar com os dados no servidor, “hashando” a senha com aquele mesmo salt que foi feito na hora de salvar. Teria que salver o salt para cada dado criptografado? ai cairia na mesma consequecia de se alguem tem acesso ao codigo/servidor de nada adiantou esse salt.

    Abraço

  12. Leandro

    Ouvi falar que já se gerou um banco com todas as possibilidades do MD5 e por mais complexo que você tente gerar uma chave ela já terá uma equivalente mais fácil já gerada (o caso de choque de chaves, por exemplo, 3 chaves geram um mesmo MD5).

  13. Rafael Wendel Pinheiro

    Olá Thiago, primeiramente gostaria de parabenizá-lo pelo ótimo post. Segundo gostaria que analizasse minha situação.

    Na minha tabela de usuários, eu salvo o usuário, senha e além disso a data de cadastro do usuário no formato YYYYmmddhhiiss.

    Se no momento de encriptação eu fizer um md5(usuario . senha . data) você acha que ficaria seguro?

    Valeu!
    Abs

    1. Thiago Belem Autor do post

      Eu não acho que seja tão seguro quanto um salt aleatório, mas é mais seguro do que encriptar apenas a senha do usuário.

      Outra questão é que o atacante, se invadir o seu site, terá o banco de dados e os arquivos em mão, e ele irá ver a sua lógica de encriptação (user + senha + data), aí é só gerar uma rainbow tabable baseado nos dados do banco de dados.

      Abs

Os comentários estão fechados.