Segurança · 9 de setembro de 2026

Segurança de senhas para desenvolvedores — o que armazenar, o que hashear e o que nunca, jamais fazer

Senhas em texto plano em um banco de dados são uma violação esperando acontecer. Aqui está como o armazenamento moderno de senhas parece, por que 'apenas hashear com SHA-256' não é suficiente e o que fazer em vez disso.

Toda divulgação de violação de dados segue o mesmo arco: “Levamos segurança a sério.” Então você lê os detalhes e descobre que senhas estavam armazenadas em texto plano, ou hasheadas com MD5, ou criptografadas com uma chave que estava no mesmo banco de dados. O problema de armazenamento de senhas está resolvido — os algoritmos existem, as bibliotecas existem, a documentação existe — e ainda assim desenvolvedores continuam errando.

Esta publicação cobre o que armazenar, o que não armazenar, a diferença entre hashear e criptografar e por que SHA-256(password) não é um hash de senha.

A regra: nunca armazene em texto plano

Se seu banco de dados for comprometido e você tiver senhas em texto plano, todo usuário que reutiliza essa senha em outros sites agora está comprometido. Não apenas seu site — o e-mail deles, o banco deles, as redes sociais deles. Armazenamento de senhas em texto plano não é um problema de segurança. É um problema moral.

Armazene um hash. Sempre.

Hashear vs criptografar

Hashear é uma transformação de uma via. Você não pode reverter. Dado hash(password), você não pode recuperar password. Você só pode verificar: hashear a entrada e comparar.

Criptografar é uma transformação de duas vias. Dado encrypt(password, key), você pode recuperar password com a chave. Se a chave for comprometida, toda senha é comprometida.

Hasheie senhas. Nunca criptografe. Se você precisa recuperar o texto plano (você não precisa), está resolvendo o problema errado.

Por que SHA-256 não é suficiente

Um erro comum: hash = SHA-256(password). É rápido, é bem conhecido e é errado para senhas.

O problema é a velocidade. SHA-256 em uma GPU moderna processa bilhões de hashes por segundo. Um atacante com um banco de dados de hashes de senhas SHA-256 pode tentar cada senha comum, cada palavra de dicionário, cada permutação, em minutos.

Algoritmos de hashing de senhas são deliberadamente lentos. Eles usam esticamento de chaves — milhares de iterações de uma função de hash — para tornar ataques de força bruta impraticáveis:

  • bcrypt — o padrão desde 1999. Fator de custo ajustável. Senha + sal → hash. Toda linguagem principal tem uma biblioteca.
  • scrypt — resistente a memória. Requer grandes quantidades de RAM para calcular, tornando ataques com GPU mais difíceis.
  • Argon2 — vencedor do Concurso de Hashing de Senhas (2015). Resistente a memória, tempo e custos de memória configuráveis. A melhor prática atual.
  • PBKDF2 — recomendado pelo NIST, amplamente disponível, usa iterações HMAC. Menos resistente a memória que Argon2, mas bem testado.

Use bcrypt, scrypt ou Argon2. Não invente seu próprio esquema. Não hasheie uma vez com SHA-256 e diga que está pronto.

Sal: por que cada senha precisa de um único

Um sal são dados aleatórios adicionados à senha antes do hasheamento. Sem sal, dois usuários com a mesma senha produzem o mesmo hash. Um atacante pode pré-computar hashes para senhas comuns (uma tabela arco-íris) e correspondê-los ao seu banco de dados.

Com um sal único por usuário, todo hash é diferente mesmo se as senhas forem idênticas. Tabelas arco-íris não funcionam. O atacante tem que força bruta cada senha individualmente.

Funções modernas de hashing de senhas (bcrypt, Argon2) geram um sal aleatório automaticamente. Você não precisa gerenciar sais separadamente. Passe a senha para a função de hash, e ela cuida do resto.

O que armazenar

Para cada usuário, armazene:

  • O hash da senha (saída de bcrypt/Argon2, que inclui o sal e os parâmetros de custo)
  • O algoritmo de hash (bcrypt, argon2, etc.)
  • Os parâmetros de custo (rodadas de bcrypt, memória/tempo de Argon2)

NÃO armazene:

  • A senha em texto plano
  • O sal separadamente (a saída da função de hash o inclui)
  • Uma “chave de criptografia” que poderia descriptografar a senha
  • Dicas de senha (vazam informações)

A saída de hash do bcrypt parece: $2b$12$LJ3m4ys3GzF4VQ.YOUR.HASH.HERE. O $2b$ identifica o algoritmo, 12 é o fator de custo e o resto é sal + hash. Para verificar uma tentativa de login, hasheie a senha submetida com os mesmos parâmetros e compare.

Fatores de custo

O fator de custo do bcrypt determina a velocidade do hash. Cada incremento dobra o tempo de computação:

  • Custo 10: ~10ms por hash (rápido para usuários, rápido para atacantes)
  • Custo 12: ~40ms por hash (razoável)
  • Custo 14: ~160ms por hash (lento para atacantes, perceptível para usuários)
  • Custo 16: ~640ms por hash (lento demais para a maioria dos fluxos de login)

O ponto ideal em 2026: bcrypt custo 12-14, Argon2 com 64MB+ de memória e 3+ iterações. O objetivo é tornar cada tentativa de hash lenta o suficiente para que força brutar bilhões de senhas leve anos, mas rápida o suficiente para que um usuário não perceba atraso no login.

Limitação de taxa

Hasheamento sozinho não é suficiente. Se um atacante pode tentar milhões de senhas por segundo contra seu endpoint de login, nem bcrypt o salvará.

Limite tentativas de login. Após 5-10 tentativas falhadas do mesmo IP ou conta, bloqueie a conta temporariamente ou exija um CAPTCHA. Isso limita ataques de força bruta a um fio.

Não revele se o nome de usuário ou senha estava errado. “Nome de usuário ou senha inválidos” é mais seguro que “Nenhuma conta encontrada com esse e-mail.” Este último diz ao atacante quais nomes de usuário existem.

Adicione bloqueio de conta após falhas sustentadas. Após 10-20 tentativas falhadas, bloqueie a conta por 15 minutos ou exija verificação por e-mail para desbloquear.

Geração de senhas

Ao gerar senhas (para usuários, chaves de API, tokens):

  • Mínimo de 12 caracteres. 8 não é mais suficiente contra hardware moderno.
  • Inclua maiúsculas, minúsculas, dígitos e símbolos. Mas comprimento importa mais que complexidade.
  • Use um CSPRNG (gerador de números pseudo-aleatórios criptograficamente seguro). Math.random() não é seguro.
  • Nunca gere padrões previsíveis. Password1! parece complexo, mas é trivialmente adivinhável.

A melhor senha é uma string gerada aleatoriamente de um CSPRNG. Use um gerenciador de senhas para armazená-la. Não peça aos usuários que lembrem senhas complexas — dê uma gerada e deixe o gerenciador cuidar.

Erros comuns

  • Hashear uma vez com SHA-256 ou MD5 — rápido demais, sem sal, trivialmente força brutável.
  • Usar um sal estático — se todos os usuários compartilham o mesmo sal, tabelas arco-íris funcionam.
  • Armazenar o sal separadamente — se o sal estiver em uma tabela diferente, um atacante que comprometer o banco de dados obtém ambos.
  • Criptografar em vez de hashear — se você pode descriptografar a senha, um atacante com a chave também pode.
  • Sem limitação de taxa — nem bcrypt é vulnerável a tentativas de login ilimitadas.
  • Revelar se um nome de usuário existe — ajuda atacantes a enumerar contas válidas.
  • Usar Math.random() para geração de senhas — previsível, não criptograficamente seguro.

Referência rápida | Faça | Não faça | |——|–––––| | Usar bcrypt, Argon2 ou scrypt | Usar SHA-256 ou MD5 | | Deixar a biblioteca cuidar do sal | Gerenciar sais manualmente | | Limitar tentativas de login | Permitir tentativas ilimitadas de senha | | Gerar senhas com CSPRNG | Usar Math.random() | | Armazenar apenas o hash | Armazenar senhas em texto plano ou criptografadas | | Dizer “credenciais inválidas” | Dizer “senha incorreta” |

Experimente

Se você precisa gerar uma senha segura aleatória ou hashear uma string para testes, use uma ferramenta baseada em navegador para que a operação permaneça local. Sua senha nunca deve tocar um servidor para uma tarefa simples de geração.