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
- 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).
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!
Muito bom , parabéns !
Muito bom o artigo sobre segurança.
Tem bastante conteúdo interessante.
Parabéns, sim, o blog é muito bom.
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
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
Bacana.
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!!
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.
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.
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 =)
Excelente artigo!
Agora, gostaria de saber como faço a parte do invasor?
Afff… que complexo mesmo hein!
Gostei… mt bom, já tinha lido o artigo original, mas não tinha entendido direito, vlw por simplificar!
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á.
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.
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.
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?
Sim Rodrigo, como respondi no outro comentário ali, a idéia é dificultar ao máximo, mas não há – nem nunca haverá – proteção 100%.
Quero ver eu fazer um sistema de digital + DNA se não fica 100% seguro!! kkk
Solução: Fujam para as colinas!
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.
@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.
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
Sim, você precisa salvar o salt pra cada usuário se quiser usar um salt único por usuário.
Agora só falta encriptar o código fonte que gera a senha!
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).
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
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
Muito bom,