Português (Brasil)
  • auth
  • senha
  • segurança
  • hashing
  • bcrypt
  • argon2
  • sha1
  • sha2
  • login
  • sign-in

A evolução do hashing de senhas

Você pode ter ouvido conselhos para escolher algoritmos de hashing de senha, mas você já parou para pensar por que eles são recomendados? Neste artigo, vamos explorar a evolução dos algoritmos de hashing de senha e as razões por trás deles.

Gao
Gao
Founder

Introdução

O hashing de senha, como o nome sugere, é o processo de calcular um valor de hash a partir de uma senha. O valor de hash é geralmente armazenado em um banco de dados e, durante o processo de login, o valor de hash da senha inserida pelo usuário é calculado e comparado com o valor de hash armazenado no banco de dados. Se eles corresponderem, o usuário é autenticado.

Antes de nos aprofundarmos na evolução dos algoritmos de hashing de senha, é 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 armazena senhas em texto simples, o hacker pode acessar diretamente sua senha. Como muitas pessoas reutilizam senhas em vários sites, o hacker pode usar esta senha para obter acesso não autorizado a suas outras contas. A situação se torna ainda pior se você usar a mesma senha ou uma senha semelhante para sua conta de e-mail, pois o hacker pode redefinir sua senha e assumir 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 extremamente sensíveis e você definitivamente não quer que ninguém tenha acesso a elas.

Armazenar senhas sem usar hashing é um erro de principiante. Infelizmente, se você procurar por "vazamento de senha em texto simples", descobrirá que grandes corporações como o [Facebook] (https://krebsonsecurity.com/2019/03/facebook-stored-hundreds-of-millions-of-user-passwords-in-plain-text-for-years /), [DailyQuiz] (https://therecord.media/8-3-million-plaintext-passwords-exposed-in-dailyquiz-data-breach) e [GoDaddy] (https://www.wordfence.com/blog/2021/11/godaddy-breach-plaintext-passwords/) já passaram por vazamentos de senhas em texto simples. É provável que muitas outras empresas tenham cometido o mesmo erro.

Codificação vs. criptografia vs. hashing

Esses três termos geralmente são confundidos, mas são conceitos distintos.

Codificação

A codificação é a primeira coisa a ser excluída para o armazenamento de senhas. Por exemplo, [Base64] (https://en.wikipedia.org/wiki/Base64) é um algoritmo de codificação que converte dados binários em uma string de caracteres:

Saber o algoritmo de codificação permite que qualquer pessoa decodifique a string codificada e recupere os dados originais:

Para os hackers, a maioria dos algoritmos de codificação é equivalente ao texto simples.

Criptografia

Antes do hashing ganhar popularidade, a criptografia foi 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 poderá descriptografar a senha e recuperar a senha em texto simples.

Hashing

A principal diferença entre hashing, codificação e criptografia é que o hashing é irreversível. Uma vez que uma senha é transformada em hash, ela não pode ser descriptografada para sua forma original.

Como proprietário do site, você realmente não precisa saber a senha em si, contanto que o usuário possa fazer login com a senha correta. O processo de registro pode ser simplificado da seguinte maneira:

  1. O usuário insere a senha.
  2. O serviço usa um algoritmo de hashing para calcular o valor de hash da senha.
  3. O serviço armazena o valor do hash no banco de dados.

Quando o usuário faz login, o processo é:

  1. O usuário insere a senha.
  2. O serviço usa o mesmo algoritmo de hashing para calcular o valor de hash da senha.
  3. O serviço compara o valor de hash com o valor de hash armazenado no banco de dados.
  4. Se os valores de hash corresponderem, o usuário é autenticado.

Ambos os processos evitam o armazenamento de senhas em texto simples e, como o hashing é irreversível, mesmo que o banco de dados seja comprometido, o hacker só pode obter valores de hash que parecem strings aleatórias.

Kit inicial de algoritmos de hashing

O hash pode parecer a solução perfeita para o armazenamento de senhas, mas não é tão simples assim. Para entender o porquê, vamos explorar a evolução dos algoritmos de hashing de senha.

MD5

Em 1992, Ron Rivest projetou o [algoritmo MD5] (https://www.rfc-editor.org/rfc/rfc1321.html), um algoritmo de digestão de mensagem que pode calcular um valor de hash de 128 bits a partir de qualquer dado. O MD5 tem sido amplamente usado em vários campos, incluindo o hashing de senhas. Por exemplo, o valor de hash MD5 de "123456" é:

Como mencionado anteriormente, o valor de 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, ele poderá calcular o valor do hash MD5 de cada combinação e compará-las com os valores de 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.

Hoje em dia, os computadores são significativamente mais poderosos do que antes, tornando fácil forçar a quebra das 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] (https://en.wikipedia.org/wiki/Secure_Hash_Algorithms) parece uma boa escolha. SHA-1 é um algoritmo de hash que gera valores de hash de 160 bits, e SHA-2 é uma família de algoritmos de hash que geram 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 de hash SHA-256 é muito mais longo do que o MD5, e também é irreversível. No entanto, há outro problema: se você já conhece o valor do hash, como o acima, e vê exatamente esse valor de hash no banco de dados, você sabe que a senha é "123456". Um hacker pode criar uma lista de senhas comuns e seus valores de hash correspondentes, e compará-los contra os valores de hash no banco de dados. Essa lista é conhecida como tabela de arco-íris.

Sal

Para diminuir os ataques de tabela de arco-íris, o conceito de sal foi introduzido. O sal é uma string aleatória que é adicionada à senha antes do hash. Por exemplo, se o sal é "sal" e você deseja usar SHA-256 para fazer o hash da senha "123456" com o sal, em vez de simplesmente fazer:

Você faria:

Como você pode ver, o resultado é completamente diferente do hashing sem sal. Normalmente, cada usuário recebe 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 de hash da senha inserida, que é então comparado ao valor de hash armazenado.

Iteração

Apesar da adição de sal, o valor de hash ainda é suscetível a ataques de força bruta à medida que o hardware se torna mais poderoso. Para dificultar, a iteração (ou seja, executar o algoritmo de hash várias vezes) pode ser introduzida. 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 ele se torna mais lento. Portanto, é necessário um equilíbrio entre segurança e desempenho.

Intervalo no meio do tempo

Vamos fazer uma pausa e resumir as características de um bom algoritmo de hashing de senha:

  • Irreversível (resistência à pré-imagem)
  • Difícil de forçar a quebra
  • Resistente a ataques de tabela de arco-íris

Como você deve ter percebido, sal e iteração são necessários para atender a todos esses requisitos. O problema é que tanto MD5 como a família SHA não foram especificamente projetados para o hashing de senhas; eles são amplamente usados para verificação de integridade (ou "digestão 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 projetados especificamente para o 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 pareça outra string aleatória, ela contém informações adicionais. Vamos quebrá-lo:

  • A primeira seção $2y indica o algoritmo, que é 2y.
  • A segunda seção $12 indica o número de iterações, que é 12. Isso significa que o algoritmo de hash será executado 212=4096 vezes (iterações).
  • A terceira seção wNt7lt/xf8wRJgPU7kK2ju é o sal.
  • A última seção GrirhHK4gdb0NiCRdsSoAxqQoNbiluu é o valor do hash.

bcrypt tem algumas limitações:

  • O comprimento máximo da senha é de 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] (https://www.password-hashing.net/) foi realizada em 2015. Pulando os detalhes, vamos nos concentrar no vencedor: Argon2.

[Argon2] (https://www.rfc-editor.org/rfc/rfc9106.html) é um algoritmo de hashing de senha projetado por Alex Biryukov, Daniel Dinu e Dmitry Khovratovich. Ele introduz vários novos conceitos:

  • Difícil para a memória: o algoritmo é projetado para ser difícil de paralelizar, tornando a força bruta com GPUs desafiadora.
  • Difícil para o tempo: o algoritmo é projetado para ser difícil de otimizar, tornando a força bruta com ASICs (Application-specific integrated circuits) difícil.
  • Resistente a ataques de canal lateral: o algoritmo é projetado para ser resistente a ataques de canal lateral, como ataques de tempo.

Existem duas versões principais do Argon2, o Argon2i e o Argon2d. Argon2i é o mais seguro contra ataques de canal lateral, enquanto Argon2d oferece a maior resistência contra ataques de quebra por GPU.

-- Argon2

Aqui está um exemplo de valor de hash Argon2:

Vamos quebrá-lo:

  • The first section $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, custo de tempo e grau de paralelismo, que são 16, 2 e 1.
  • A quarta seção $YTZ5ZnpXRWN5SlpjMHBDRQ é o sal.
  • A última seção $12oUmJ6xV5bIadzZHkuLTg é o valor do hash.

No Argon2, o comprimento máximo da senha é de 232-1 bytes, o sal é limitado a 232-1 bytes e o valor de hash é limitado a 232-1 bytes. Isso deve ser suficiente para a maioria dos cenários.

Argon2 está agora disponível em muitas linguagens de programação, como [node-argon2] (https://github.com/ranisalt/node-argon2/) para Node.js e [argon2-cffi] (https://pypi.org/project/argon2-cffi/) para Python.

Conclusão

Ao longo dos anos, os algoritmos de hashing de senha sofreram uma evolução significativa. Devemos um agradecimento à comunidade de segurança por suas décadas de esforço em tornar a internet um lugar mais seguro. Graças às suas contribuições, os desenvolvedores podem prestar mais atenção na construção de melhores serviços sem se preocupar com a segurança do hashing de senhas. Embora alcançar 100% de segurança em um sistema possa ser inatingível, podemos empregar diversas estratégias 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. Nós fornecemos soluções seguras (usamos Argon2!), confiáveis e escaláveis, permitindo que você se concentre na construção de seu produto.