Guia para armazenar senhas no banco de dados de forma decente e segura

Rafael Soares
Training Center
Published in
9 min readAug 22, 2018

Antes: se você é mais de vídeo do que de texto, o conteúdo deste artigo está todo aqui: https://youtu.be/HMrffmy9DIQ

A ideia por trás de armazenar senhas no banco de dados de forma segura é pensando no caso dos seus dados vazarem e impedir que estas senhas sejam quebradas. Se o vazamento de dados não representasse um problema, bastava salvar as senhas em plain text e pronto. Portanto, nem vou falar aqui sobre não fazer isso, ok?

Antes de falar sobre como armazenar as senhas, vamos explorar algumas formas de se quebrar senhas já em forma de hash.

Como armazenar senhas no banco de dados

Quebrando senhas

Nem preciso dizer que você não deve fazer isso contra um banco de dados pertencente a alguém que não está de acordo com isto, né? É sério, o propósito deste artigo é completamente didático e com o intuito de você usar isso pra segurança. Se você fizer qualquer tipo de ataque cibernético sem permissão, além de ser crime, é só questão de tempo pra descobrirem você e isso acabar com sua vida. Então, não faça…

Como mencionei, a gente precisa pensar sempre que nossa tabela de senhas vazou de alguma forma (que não é nada difícil, vamos ser sinceros).

Recentemente, eu tive acesso a uma lista de senhas (sem e-mails) de um site de música muito grande, que teve seu banco vazado em 2012. O mais curioso é que as senhas estavam em MD5.

Fica a dica: aquela sua senha que não é muito forte pode estar sendo salva num algoritmo fraco em um dos sites que você acessa. Uma vez que o banco de dados vaza, basta quebrarem sua senha e testarem a combinação e-mail + senha em vários sites famosos e conseguirem muitos acessos.

Como usuário, você também precisa construir senhas “inquebráveis” e se possível uma senha diferente pra cada site que você cadastra. Eu uso o 1password pra isso, mas existem várias outras opções.

Como desenvolvedor, se você ainda acha que não tem nada de errado em usar MD5 no seu site, recomendo esse vídeo: https://youtu.be/jEDm2w_3BVY

Vasculhando

A primeira coisa que a gente pode fazer com uma lista de hash em mãos é agrupar os hashes iguais e ordenar pelos que aparecem mais vezes. Na vida real, se você encontra dois, três ou mais hashes iguais, significa que provavelmente é alguma senha comum e não utilizam nenhuma informação personalizada, como "123admin". Mesmo sem um programa de quebra de senha a gente já pode pegar esse hash e procurar no google. Se for uma senha comum, provavelmente você já vai achar a forma plaintext.

Hashcat

Pra aplicar algumas técnicas de quebra de senhas, eu gosto de uma ferramenta chamada hashcat. Ela permite que você utilize diferentes técnicas e em cima de 200+ algoritmos diferentes de hash, podendo usar CPU e GPU.

Essa gif abaixo foi extraído de um pequeno vídeo que eu fiz, em que eu quebro várias senhas deste site que mencionei acima. Eu to utilizando um notebook Core i5 com uma péssima GPU e olha a velocidade em que ele consegue quebrar senhas fazendo um brute force com 6 letras + 2 números. De acordo com o status do próprio hashcat, a velocidade de tentativas era de 101.4 milhões de hashes por segundo.

Quebrando senhas MD5 com Hashcat

Diante deste cenário horroroso, o que fazer?

Segurança no formulário

Antes de falar sobre o banco de dados em si, tem duas coisas importantes sobre os formulários de cadastro.

Não limite o tipo de caracter

Primeiro, nunca limitar os tipos de caracteres (caracteres especiais, por exemplo) que um usuário pode escolher pra senha. Muitos sites fazem isso com medo de sofrer SQL Injection. O problema é que você se protege de um lado, mas de outro deixa as senhas dos seus usuários fracas. Ou seja, ao invés de fazer uma gambiarra pra se proteger, corrige teu código pra ele não sofrer nenhum ataque de injeção.

Limite a quantidade

Outra coisa importante é limitar o número de caracteres (no front e backend) pra senha. Um dos atributos importantes de um algoritmo de hash, pra armazenamento de senhas, é não ser rápido demais. Isso aumenta o custo de uma senha ser quebrada.

O problema é que isso pode ser usado contra você num ataque de DDoS. Se uma pessoa consegue inserir uma senha com milhares de caracteres e fizer isso várias vezes criando vários cadastros ou logins simultâneos, seu servidor vai sofrer muito tendo que hashificar essas senhas. Portanto, um número razoável para você limitar o número de caracteres do usuário, de acordo com a OWASP, é 160 caracteres.

Um bom algoritmo de hash

Salt

O atributo mais importante de um algoritmo é a utilização da técnica do salt.

Pra quem não sabe, o salt é um valor randômico, único, com tamanho fixo. E quando digo único, quero dizer único universalmente, não apenas no sistema ou na base de dados.

O salt deve ser concatenado com a senha do usuário e enviado a uma função de hash. Com este resultado em “mãos” você vai salvar no seu banco de dados o salt gerado pra essa senha e mais o resultado hashificado.

Na prática, um pseudo-código seria algo como:

resultado = salt + func_hash([salt] + [senha])

Exemplo:

*9o3$.$a&2*a = *9o3$ + hashify(*9o3$ + 123456)

Ou seja, se outro usuário criar uma mesma senha, o fato de você gerar sempre um novo salt fará com que o resultado seja completamente diferente.

Também, como você viu, o salt fica visível junto com o hash. Ou seja, não confunda o salt com chave de criptografia que é um valor que deve ficar escondido. São coisas diferentes.

A técnica do salt serve para duas coisas:

  1. Prevenir ataque de colisão, ou seja, o atacante não vai poder agrupar e ordenar os hashes iguais pelos que mais aparecem, porque nenhum hash será igual (mesmo que os usuários tenham senhas iguais) e consequentemente será custoso fazer um ataque de dicionário;
  2. Aumentar o nível de proteção das senhas sem depender do usuário pra criar uma senha complexa (o que não exclui a necessidade do usuário precisar escolher uma boa senha).

Baixa velocidade

Um algoritmo que é muito rápido gerando hashes pode soar algo bom, afinal sua aplicação vai ficar ainda mais rápida, certo? Mas quem gosta muito disso também são as ferramentas de quebra de senha.

Como você viu acima, quando utilizei o hashcat pra quebrar senhas MD5, ele conseguiu gerar 101.4 milhões de tentativas por segundo, e isso é porque o algoritmo MD5 é extremamente rápido.

Ao mesmo tempo, você também não quer um algoritmo que demore minutos pra criar apenas um hash, senão seria inviável usar em qualquer tipo de aplicação. Ou seja, é complicado.

Essa velocidade vai sempre depender do nível de processamento das CPUs e GPUs atuais, e apenas isso já faz com que depois de um tempo os algoritmos de hoje sejam obsoletos amanhã. Vide o MD5 e o SHA-1. Quando foram criados, a velocidade não era tão rápida devido ao hardware da época.

É importante frisar que estamos falando sobre gerar hashes para senhas. Pode ser que em outra outra ocasião utilizar MD5 seja bom, quando você não tem muita preocupação com a integridade do hash.

Custo de memória

Um bom algoritmo, preferencialmente, tem um custo razoavelmente alto de memória RAM. Assim como não queremos algoritmos rápidos, também queremos que, pra fazer o ataque, a pessoa tenha que ter recursos altos de memória, ou sua tentativa de quebra de senha vai demorar centenas de anos.

Mas nada disso você vai desenvolver sozinho. Já existem algoritmos testados e analisados por vários matemáticos e especialistas de segurança, então por favor, não tente desenvolver seu próprio algoritmo de hash. Use funções prontas que já utilizem a técnica do salt e que tenham uma boa velocidade de processamento para se defender do atacante e para ser usada na sua aplicação.

Os melhores algoritmos

Essa lista é baseada nos algoritmos recomendados pela OWASP.

Também, todos estes algoritmos já possuem implementações pras linguagens de programação mais populares, ou seja, não tem desculpa.

Argon2

Atualmente, o algoritmo de hash mais recomendado é o Argon2. Ele é o ganhador da Password Hashing Competition, uma competição aberta que rolou entre 2013 e 2015 organizada por vários especialistas de segurança do mundo inteiro de diversas universidades e organizações de segurança mundialmente reconhecidas. Ainda hoje é o algoritmo mais recomendado.

Ele permite você configurar o custo de memória RAM pra se gerar o hash, o custo de tempo que é dado em número de iterações, o tamanho do hash gerado no final e o número de threads que ele deve criar pra gerar o hash.

PBKDF2

Se por algum motivo você não puder utilizar a Argon2, quando, por exemplo, uma certificação de segurança como a FIPS é exigida ou outra exigência corporativa, você deve escolher o algoritmo PBKDF2. Foi criado pela RSA Security em 2000, aquela empresa criadora do famoso algoritmo de criptografia RSA.

Em 2017 ele ainda foi o algoritmo mais recomendado pela IETF na RFC 8018. Apesar disso, ele recebe bastante críticas por causa do seu baixo custo de memória RAM (algo que o Argon2 e scrypt se propuseram a resolver).

Ele permite configurar o número de iterações necessárias pra gerar o hash e o tamanho da chave final.

scrypt

Mas, caso você não puder utilizar a Argon2 e também não precisar de qualquer certificação ou suporte específico, utilize o scrypt ao invés do PBKDF2.

Foi publicado em 2016 e a grande diferença do scrypt pro PBKDF2, é que o scrypt foi criado pra se proteger contra paralelismo, exigindo uma quantidade grande de memória pro processamento, coisa que o PBKDF2 não se preocupa.

Ele permite configurar a quantidade de memória RAM e o tamanho da chave final.

bcrypt

Agora, se você não puder utilizar nenhuma destas 3 alternativas, escolha o bcrypt.

Foi publicado em 2014 e é o padrão utilizado pelo OpenBSD pra testar integridade de arquivos e algumas distribuições de Linux.

Assim como os outros, ele também utiliza Salt (apesar de não permitir que você crie o salt por você próprio) e permite que você configure o número de iterações na criação do hash.

Uma segurança a mais com criptografia

Você também pode optar por utilizar um algoritmo com código de autenticação. Apesar desta técnica ser utilizada para encriptação de dados que seriam depois desencriptados pra serem lidos por um humano, ela também pode ser utilizada para encriptar um hash.

Neste caso, você precisa ter uma chave em algum lugar que não esteja ao alcance de quem, hipoteticamente, poderia ter acesso ao seu banco de dados ou ao código da sua aplicação num ataque de invasão.

Esta chave seria utilizada pra desencriptar os hashes salvos no seu banco e só depois você fazer a verificação do password. Ou seja, mesmo que seu banco de dados vaze, sem a chave não tem programa de quebra de senhas que dê jeito.

HMAC

Novamente, não desenvolva seu próprio algoritmo ou sua própria técnica para esta última alternativa. Utilize as que já são comprovadamente seguras o suficiente para dificultar um ataque.

Um dos algoritmos mais utilizados é o HMAC, que você pode inclusive utilizar em conjunto com o PBKDF2 (suporte nativo). Muitas linguagens já possuem bibliotecas que fazem isto tudo pra você com apenas uma função, sem que você tenha que cuidar manualmente da encriptação e da hashificação separadamente.

Lidando com legado

Caso você esteja lidando com uma aplicação ou banco de dados que utiliza um algoritmo ineficiente para salvar as senhas ou outros dados sensíveis no banco de dados, você pode dar uma olhada neste artigo: https://veggiespam.com/painless-password-hash-upgrades/

Ele é muito bom e fala exatamente sobre como atualizar seu sistema legado de armazenamento de senhas sem muita dor de cabeça. O artigo é de Janeiro de 2018.

Se você não quiser utilizar nenhuma dessas alternativas, ou outra comprovadamente tão segura quanto essas, bem, aí eu diria que você estará sendo negligente e estará colocando as senhas de seus usuários em grande risco.

Um agradecimento especial ao Training Center por revisar este artigo!

Se você gostou, não deixa de dar um clap, isso vai fazer chegar a outras pessoas e ajudar ainda mais gente ❤ =)

Obrigado por chegar até aqui e tô arguardando seu feedback.

Um abraço!

--

--

Rafael Soares
Training Center

Twitter @rafaeltravel88 | Software developer @smart-pension-technology | Engenharia Reversa @ YouTube | Learning everyday @trainingcenter