O que fazer antes de armazenar ou usar senhas na sua aplicação? Ah, o clássico tema que sempre gera debates burros e acalorados em qualquer time de desenvolvimento, quase um campeonato de quem é mais ignorante no café da manhã.

Este é o primeiro artigo de uma série sobre Autenticação. Aqui vamos direto ao ponto: como armazenar senhas de forma segura. Parece básico, mas é impressionante como ainda tem gente tratando senha como se fosse dado comum, quase um CPF em texto puro no banco.

Imagem de segurança

Por que armazenar de maneira segura?

A maior parte dos ciberataques tem um ponto em comum: gente de dentro da empresa. Às vezes por má intenção, às vezes por pura negligência. Quem já passou por um processo de certificação PCI/DSS sabe bem boa parte das regras existe justamente para proteger o sistema dos próprios funcionários, especialmente os de TI.

E aí chegamos no calcanhar de Aquiles: senhas.

Senha é a ruína dos desenvolvedores. Autenticação parece simples, mas é absurdamente complexa, e boa parte dessa complexidade está em como armazenar a senha de forma segura.

A recomendação é clara: sempre use um sistema ou framework existente. Não invente moda. Se você tá lendo esse texto é porque não é especialista em Autenticação, portanto lembre-se: Autenticação feita por amadores é autenticação amadora. E adivinha? Atacantes adoram isso.

Agora, se não houver escolha e você realmente precisar implementar algo por conta própria, vem a pergunta inevitável: qual é a forma correta de armazenar senhas no seu sistema?

O jeito errado

Uma das melhores formas de aprender a fazer certo é começar pelo errado. Saber o que não deve ser feito já evita boa parte das burradas.


Plain Text

Armazenar senha em texto puro é a pior decisão possível. Simples assim. Ninguém deveria abrir uma tabela do banco e ver todas as senhas de cara, como se fosse planilha de Excel. Quer deixar a vida do atacante ainda mais fácil? Então já exporta direto pro Pastebin e manda o link no Slack da empresa.

Encoded

Quase nunca escrevo em primeira pessoa, mas aqui abro exceção.

Base64? Base32? Sério (PQP), prefiro ver senha em texto puro. Pelo menos fica claro que a pessoa não se deu ao trabalho de fingir que se importava. Quando vejo uma senha “protegida” em Base64, a frustração é dupla: o autor quis parecer cuidadoso, mas não pesquisou nem o mínimo.

E não pense que é caso isolado, não. Já vi essa decisão ser tomada mais de uma vez em projetos diferentes. Toda vez a sensação é a mesma: “se era pra fazer errado, ao menos não precisava perder tempo codificando”.

Criptografada

Aqui já parece um avanço em relação ao encoding... mas continua ó: Uma bosta.

Criptografia é uma via de mão dupla: tudo que é criptografado pode ser descriptografado, basta ter a chave. E aí vem a pergunta incômoda: onde essa chave vai ficar guardada?

O problema fundamental é esse: para validar uma senha, você precisaria descriptografar o valor armazenado. E se um atacante conseguir acesso à chave, pronto, todas as senhas vão embora de brinde.

E se você ficou chocado: "como assim criptografia não é recomendada para senhas?". Só prova que você não deveria continuar fazendo esse sistema de autenticação.

underworld

Criptografia e Hashing

É comum ouvir dev falando em “senha criptografada”, quando na verdade está lidando com uma senha com hash. A confusão é clássica, mas perigosa.

Criptografia é transformar texto legível em algo ilegível usando uma chave.

  • Com a chave certa (ou com a chave relacionada, no caso da criptografia assimétrica), o processo pode ser revertido.
  • Ou seja: se está criptografado, alguém pode descriptografar.

Hashing, por outro lado, não tem chave. É como um moedor de carne: depois que você passa a vaca, não dá mais pra recuperar o animal. Só fica o resultado final, irreversível.

Funções de hash são determinísticas: a mesma entrada sempre gera a mesma saída. Qualquer um pode calcular o hash de uma entrada arbitrária, e é justamente isso que permite comparar senhas de forma segura.

E aí vem o detalhe prático:

  • Se o algoritmo é MD5 ou alguma variante da família SHA (SHA-1, SHA-2, SHA-3 etc.), estamos falando de hashing, não de criptografia.
  • (Agora, se você ainda guarda senha com MD5 e acha que está seguro... bem, melhor nem comentar).

Hash – O jeito certo

Depois que a senha passa por hashing, ela ainda continua útil. Sim, mesmo sendo irreversível. O truque está no fato de que, para a mesma entrada, o hash sempre produz o mesmo resultado.

Isso significa que:

  • Se o usuário digita a senha correta, o hash calculado na hora será igual ao armazenado.
  • Se digitar errado, o valor será diferente.
  • A probabilidade de duas senhas distintas gerarem o mesmo hash? Tão baixa que, na prática, é mais fácil ganhar na mega-sena por 5x seguidas.

Como o hashing é determinístico, basta armazenar o valor do hash para validar senhas. Simples, eficiente e, veja só, não exige guardar nada em texto puro nem sair escondendo chave de criptografia em algum lugar obscuro do sistema.

Ou seja: uma senha com hash é suficiente para verificar se está correta, sem drama e sem gambiarra.. Mas calma ai a formiga no toba. Não é qualquer algoritmo de Hash.

Por que hash?

O atacante pode ter conseguido as senhas de várias formas: um SQL injection, uma fita de backup perdida, um DBA mal-intencionado ou um dev com acesso descuidado ao banco. Neste artigo não vamos discutir como o invasor entrou, o fato é que entrou. E quando isso acontece, você já tem um problema grave nas mãos.

O hash entra como segunda linha de defesa. O invasor já passou pela primeira barreira (o acesso ao repositório de dados). O objetivo do hashing é tornar muito mais difícil transformar aquele dump em senhas legíveis.

Quando um atacante fica com os hashes, o próximo passo natural dele é tentar descobrir as senhas reais, via brute force, dictionary attack ou ataques por GPU. Aplicar uma função de hash correta transforma esse trabalho em algo matematicamente impraticável: com o algoritmo e parâmetros adequados, inferir a senha original a partir do hash torna-se extremamente difícil.

Ou seja: com hashes bem feitos você pode armazenar esses valores no banco com muito mais segurança, não porque esteja imune, mas porque elevou o custo ($$$) para o atacante.

Basta utilizar hash e estou seguro?

Claro que não. Se fosse tão simples, não existiriam milhões de senhas vazadas por aí com hash bonitinho em MD5 ou SHA1. O problema não é usar hash, é qual hash você escolhe.

Nem todo algoritmo de hash serve para senhas. Para começar, um algoritmo adequado precisa ser lento. Isso mesmo: lento de propósito. Por quê? Porque ataques com hardware especializado (GPUs, ASICs) adoram funções rápidas. Se o seu hash é instantâneo, o atacante consegue testar milhões de combinações por segundo.

Na segunda parte deste artigo vamos ver exatamente isso:

  • por que hashes rápidos são vulneráveis,
  • quais são os ataques mais comuns,
  • e quais algoritmos realmente fazem sentido para proteger senhas.

(Spoiler: se você ainda acha que SHA256 puro vai te salvar, recomendo se preparar para a decepção.)

Referências