Skip to content

031. Hash das passwords

Filipe Deschamps edited this page Jan 26, 2022 · 1 revision

26/01/22

Escolha do módulo para hash

O PR #162 implementa o hash das senhas, e utiliza um método de criptografia bem popular chamado bcrypt... é quase que um padrão na comunidade de Node.js. Mas escolher o módulo que implementa esse método foi interessante, porque o padrão de todos os tutoriais é usar o bcrypt, porém há uma alternativa mais popular chamada bcryptjs que faz a exata mesma implementação, mas utilizando apenas JavaScript, ao invés de depender da compilação de binários como faz o bcrypt. O tradeoff em se usar a alternativa em JavaScript é uma perda de 30% de performance na hora de calcular os hashes (o que pessoalmente achei pouco até), mas ganhar compatibilidade total em qualquer ambiente Node.js que você for rodar o código, o que pode não acontecer com o bcrypt. Como vamos rodar na Vercel onde não queremos nos importar com o ambiente, achei prudente aceitar o tradeoff e mirar na compatibilidade.

Escolha do número de rounds do salt

O método de criptografia bcrypt usa uma estratégia muito legal, que é consumir o potencial de processamento para criar o hash e o quão difícil é fazer isso é definido pela propriedade round do salt (que por conveniência acaba sendo concatenado na própria string da hash). E quanto maior o round e mais difícil computar, também fica mais difícil para alguém quebrar um hash caso aconteça o vazamento desse dado. O problema é que você pode aumentar tanto esse valor que ficará inviável para os dois lados o hacker e o seu sistema, que irá suar a cada criação ou verificação de senha.

Nos meus testes locais e sem utilizar nenhuma estratégia de hash, a bateria de testes automatizados rodavam em cerca de 2 segundos. Agora olha a tabela abaixo que interessante que o jogo de "aumentar a dificuldade dos rounds" começa a ficar. Na esquerda da tabela temos os rounds e na direita o tempo que levou para concluir a bateria de testes (use como referência os 2 segundos anteriores sem hash algum).

rounds tempo
1 2s
5 2s
10 4s
11 7s
12 12s
13 22s
14 44s
15 93s

Esse não é um teste científico, porque minha máquina não estava num estado "em branco", mas mesmo assim, o impacto na performance é impressionante e nos testes com os últimos rounds, a minha máquina ligou a ventoinha e me chamou pra conversar.

Cavucando na internet, não há um consenso exato de quantos rounds utilizar. Há cálculos que levam em consideração a evolução dos processadores, e também um certo chute da galera de usar entre 10 rounds em post mais antigos e 12 rounds em posts mais novos.

Minha sugestão é: para o ambiente de produção, vamos usar 14 rounds e ver como o sistema se comporta (até porque, isso pode penalizar a experiência do usuário). E eu destaquei o ambiente de produção, porque eu comecei a odiar rodar os testes automatizados localmente, mesmo isolando só o api/v1/user por exemplo... e isso não é um bom sinal. Sair de "2 segundos" para "44 segundos" não foi uma sensação boa. Aí tive a ideia de separar os ambientes, onde em produção deixar os 14 mas em ambiente local deixar 1. O bom é que o tamanho do hash gerado é sempre o mesmo, independente do número de rounds.

3 preocupações

São preocupações naturais, mas que vou escrever aqui para deixarmos a anteninha em pé:

  1. Um bug no módulo password.js ou mau configuração no ambiente de produção que comece a fazer os hashes serem gerados com apenas 1 round. Pelo menos no código eu primeiro seto a variável em 14 e verificando o ambiente eu faço a retribuição do valor para 1.
  2. Mau configuração no banco de dados que não está dentro de uma VPC e precisa estar exposto de forma pública para que a Vercel consiga acessar.
  3. Brechas no sistema de autenticação e autorização.