A evolução do hashing de senhas
Você pode ter ouvido conselhos para escolher algoritmos de hashing de senhas, mas já pensou porque eles são recomendados? Neste artigo, vamos explorar a evolução dos algoritmos de hashing de senhas e as razões por trás deles.
Introdução
Hashing de senha, como o nome sugere, é o processo de calcular um valor de hash a partir de uma senha. O valor do hash é normalmente armazenado em um banco de dados e, durante o processo de login (início de sessão), o valor do hash da senha inserida pelo usuário é calculado e comparado com o valor do hash armazenado no banco de dados. Se corresponderem, o usuário está autenticado.
Antes de nos aprofundarmos na evolução dos algoritmos de hashing de senhas, é importante entender por que isso é necessário.
Senhas em texto simples: Um grande risco de segurança
Imagine ser um usuário de um site onde você registrou uma conta. Um dia, o site é hackeado, e o banco de dados é vazado. Se o site armazenar senhas em texto simples, o hacker pode ter acesso direto à sua senha. Como muitas pessoas reutilizam senhas em vários sites, o hacker pode usar essa senha para obter acesso não autorizado em suas outras contas. A situação se torna ainda pior se você usar a mesma ou uma senha semelhante para sua conta de email, pois o hacker pode redefinir sua senha e assumir o controle de todas as suas contas associadas.
Mesmo sem uma violação de dados, em grandes equipes, qualquer pessoa com acesso ao banco de dados pode ver as senhas. Comparado com outras informações, as senhas são altamente sensíveis, e você definitivamente não quer que ninguém tenha acesso a elas.
Armazenar senhas sem hashing é um erro de novato. Infelizmente, se você pesquisar por "vazamento de senha em texto simples", você encontrará que grandes corporações como Facebook, DailyQuiz, e GoDaddy já vazaram todas as senhas em texto simples. É provável que muitas outras empresas tenham cometido o mesmo erro.
Codificação vs. Criptografia vs. Hashing
Estes três termos são frequentemente confundidos, mas são conceitos distintos.
Codificação
Codificação é a primeira coisa a excluir para armazenamento de senha. Por exemplo, Base64 é um algoritmo de codificação que converte dados binários em uma string de caracteres:
Saber o algoritmo de codificação permite a qualquer um decodificar a string codificada e recuperar os dados originais:
Para os hackers, a maioria dos algoritmos de codificação são equivalentes a texto simples.
Criptografia
Antes do hashing ganhar popularidade, a criptografia era usada para armazenar senhas, como com o AES. A criptografia envolve o uso de uma chave (ou um par de chaves) para criptografar e descriptografar dados.
O problema com a criptografia é evidente no termo "descriptografar". A criptografia é reversível, o que significa que se um hacker obtiver a chave, ele pode descriptografar a senha e recuperar a senha em texto simples.
Hashing
A principal diferença entre o hashing, codificação e criptografia é que o hashing é irreversível. Uma vez que uma senha é convertida em hash, ela não pode ser descriptografada de volta à sua forma original.
Como proprietário de um site, você realmente não precisa saber a senha em si, desde que o usuário possa fazer login com a senha correta. O processo de registro pode ser simplificado da seguinte forma:
- Usuário insere a senha.
- O serviço usa um algoritmo de hashing para calcular o valor do hash da senha.
- O serviço armazena o valor do hash no banco de dados.
Quando o usuário faz o login, o processo é:
- Usuário insere a senha.
- O serviço usa o mesmo algoritmo de hashing para calcular o valor do hash da senha.
- O serviço compara o valor do hash com o valor do hash armazenado no banco de dados.
- Se os valores do hash correspondem, o usuário está autenticado.
Ambos os processos evitam armazenar senhas em texto simples, e como a hashing é irreversível, mesmo que o banco de dados seja comprometido, o hacker só pode obter valores de hash que aparecem como strings aleatórias.
Starter pack de algoritmos de hashing
O hashing pode parecer a solução perfeita para o armazenamento de senhas, mas não é tão simples. Para entender por quê, vamos explorar a evolução dos algoritmos de hashing de senhas.
MD5
Em 1992, Ron Rivest projetou o algoritmo MD5, um algoritmo de digest de mensagem que pode calcular um valor de hash de 128 bits de qualquer dado. O MD5 tem sido amplamente usado em vários campos, incluindo hashing de senhas. Por exemplo, o valor do hash MD5 de "123456" é:
Como mencionado anteriormente, o valor do hash aparece como uma string aleatória e é irreversível. Além disso, o MD5 é rápido e fácil de implementar, tornando-o o algoritmo de hashing de senha mais popular.
No entanto, as vantagens do MD5 também são suas fraquezas no hashing de senhas. Sua velocidade o torna vulnerável a ataques de força bruta. Se um hacker possuir uma lista de senhas comuns e suas informações pessoais, eles podem calcular o valor do hash MD5 de cada combinação e compará-los com os valores do hash no banco de dados. Por exemplo, eles podem combinar seu aniversário com seu nome ou o nome do seu animal de estimação.
Na atualidade, os computadores são significativamente mais poderosos do que antes, tornando fácil a força bruta em hashes de senhas MD5.
Família SHA
Então, por que não usar um algoritmo diferente que gera valores de hash mais longos? A família SHA parece uma boa escolha. SHA-1 é um algoritmo de hashing que gera valores de hash de 160 bits, e SHA-2 é uma família de algoritmos de hashing que gera valores de hash de 224 bits, 256 bits, 384 bits e 512 bits. Vamos ver o valor de hash SHA-256 de "123456":
O valor do hash SHA-256 é muito mais longo do que o MD5, e também é irreversível. No entanto, há outro problema: se você já sabe o valor do hash, como o acima, e vê o valor do hash exato no banco de dados, sabe que a senha é "123456". Um hacker pode criar uma lista de senhas comuns e seus valores de hash correspondentes, e compará-los com os valores de hash no banco de dados. Esta lista é conhecida como tabela arco-íris.
Sal
Para mitigar os ataques de tabela de arco-íris, foi introduzido o conceito de sal. O sal é uma string aleatória que é adicionada à senha antes do hashing. Por exemplo, se o sal for "salt", e você quiser usar SHA-256 para calcular o hash da senha "123456" com o sal, em vez de simplesmente fazer:
Você faria:
Como você pode ver, o resultado é completamente diferente de fazer hash sem sal. Tipicamente, cada usuário é atribuído um sal aleatório durante o registro, que é armazenado no banco de dados junto com o valor de hash. Durante o processo de login, o sal é usado para calcular o valor do hash da senha inserida, que é então comparado ao valor do hash armazenado.
Iteração
Apesar da adição de sal, o valor do hash ainda é suscetível a ataques de força bruta à medida que o hardware se torna mais poderoso. Para dificultar isso, pode ser introduzida a iteração (i.e., executar o algoritmo de hashing várias vezes). Por exemplo, em vez de usar:
Você poderia usar:
Aumentar o número de iterações torna a força bruta mais difícil. No entanto, isso também afeta o processo de login, pois se torna mais lento. Portanto, é necessário um equilíbrio entre segurança e desempenho.
Intervalo no meio do caminho
Vamos dar uma pausa e resumir as características de um bom algoritmo de hashing de senha:
- Irreversível (resistência a pré-imagem)
- Difícil de forçar a barra
- Resistente a ataques de tabela arco-íris
Como você deve ter notado, sal e iteração são necessários para satisfazer todos esses requisitos. O problema é que tanto MD5 como a família SHA não foram especificamente projetados para hashing de senha; eles são amplamente usados para verificações de integridade (ou "digest de mensagem"). Como resultado, cada site pode ter sua própria implementação de sal e iteração, tornando a padronização e a migração desafiadoras.
Algoritmos de hashing de senha
Para resolver esse problema, vários algoritmos de hashing foram especificamente projetados para hashing de senha. Vamos dar uma olhada em alguns deles.
bcrypt
bcrypt é um algoritmo de hashing de senha projetado por Niels Provos e David Mazières. Ele é amplamente usado em muitas linguagens de programação. Aqui está um exemplo de valor de hash bcrypt:
Embora apareça como outra string aleatória, contém informações adicionais. Vamos dividir isso:
- A primeira seção
$2y
indica o algoritmo, que é2y
. - A segunda seção
$12
indica o número de iterações, que é12
. Isto significa que o algoritmo de hashing será executado 212=4096 vezes (iterações). - A terceira seção
wNt7lt/xf8wRJgPU7kK2ju
é o sal. - A última seção
GrirhHK4gdb0NiCRdsSoAxqQoNbiluu
é o valor do hash.
O bcrypt tem algumas limitações:
- O comprimento máximo da senha é 72 bytes.
- O sal é limitado a 16 bytes.
- O valor do hash é limitado a 184 bits.
Argon2
Dado os debates e limitações dos algoritmos de hashing de senha existentes, uma competição de hashing de senha foi realizada em 2015. Pulando os detalhes, vamos nos concentrar no vencedor: Argon2.
Argon2 é um algoritmo de hashing de senha projetado por Alex Biryukov, Daniel Dinu, e Dmitry Khovratovich. Ele introduz diversos novos conceitos:
- Resistente à memória: O algoritmo é projetado para ser difícil de paralelizar, tornando a força bruta com GPUs desafiadora.
- Resistente ao tempo: O algoritmo é projetado para ser difícil de otimizar, tornando a força bruta com ASICs (Circuitos integrados específicos de aplicação) difícil.
- Resistente a canais laterais: O algoritmo é projetado para ser resistente a ataques de canal lateral, como ataques de tempo.
Existem duas versões principais de Argon2, Argon2i e Argon2d. Argon2i é o mais seguro contra ataques de canal lateral, enquanto Argon2d proporciona a maior resistência contra ataques de quebra de GPU.
-- Argon2
Aqui está um exemplo de um valor de hash Argon2:
Vamos dividi-lo:
- A primeira seção
$argon2i
indica o algoritmo, que éargon2i
. - A segunda seção
$v=19
indica a versão, que é19
. - A terceira seção
$m=16,t=2,p=1
indica o custo de memória, o custo de tempo, e o grau de paralelismo, onde são16
,2
, e1
. - A quarta seção
$YTZ5ZnpXRWN5SlpjMHBDRQ
é o sal. - A última seção
$12oUmJ6xV5bIadzZHkuLTg
é o valor do hash.
No Argon2, o comprimento máximo da senha é 232-1 bytes, o sal é limitado a 232-1 bytes, e o valor do hash é limitado a 232-1 bytes. Isso deve ser suficiente para a maioria dos cenários.
O Argon2 agora está disponível em muitas linguagens de programação, como node-argon2 para Node.js e argon2-cffi para Python.
Conclusão
Ao longo dos anos, os algoritmos de hashing de senhas passaram por uma evolução significativa. Devemos nossa gratidão à comunidade de segurança por suas décadas de esforço para tornar a internet um lugar mais seguro. Graças às suas contribuições, os desenvolvedores podem prestar mais atenção à construção de melhores serviços sem se preocupar com a segurança do hashing de senhas. Embora alcançar uma segurança 100% em um sistema possa ser inatingível, podemos empregar estratégias diversas para minimizar os riscos associados.
Se você gostaria de evitar o incômodo de implementar autenticação e autorização, sinta-se à vontade para experimentar o Logto gratuitamente. Nosotros fornecemos soluções seguras (nós usamos Argon2!), confiáveis e escaláveis, permitindo que você se concentre em construir seu produto.