Introdução
Proteger as senhas do site é uma habilidade essencial que qualquer desenvolvedor deve ter. JavaScript
oferece uma opção para garantir o armazenamento seguro e o processamento de senhas ou outros dados sensíveis usando os algoritmos de hash fornecidos pelo módulo BcryptJS
do JavaScript
.
Neste tutorial, você aprenderá sobre o BcryptJS
e o hash para configurar um servidor express básico que armazenará senhas como hashes em um banco de dados em vez de cadeias de caracteres brutas e os recuperará para autenticar a senha.
Pré-requisitos
Para continuar com este tutorial, você deve ter a seguinte configuração.
-
Versão estável do Node.js instalada em seu computador com a versão 12.x ou superior. Você pode usar este tutorial do DigitalOcean para instalar a versão mais recente do Node Js em seu computador.
-
Você deve saber como programar em JavaScript.
-
_Você deve ter o Express JS instalado em seu computador. Você pode usar este guia para aprender como configurar um servidor Express.
-
Por último, você precisará do banco de dados MongoDB Community ou Atlas para completar este tutorial. Você pode instalá-lo usando um desses guias da DigitalOcean sobre_ como instalar o MongoDB.
Por que usar o BcryptJS?
O Bcrypt é um algoritmo de hash para criar hashes de senhas e armazená-las em caso de violação de dados. Esse algoritmo de hash avançado usa salts, tornando difícil de ser quebrado por ataques como força bruta.
BcryptJS é a implementação em JavaScript do algoritmo de hash Bcrypt, permitindo usar a criptografia de hash sem ter que lidar com funções de hash complexas. Algumas das razões que tornam o BcryptJS uma ótima escolha para a segurança de senhas são as seguintes:
-
Segurança – O BcryptJS implementa o algoritmo Bcrypt, um algoritmo lento (o que é bom para a hash) e requer uma intensa potência computacional. Isso torna uma tarefa rigorosa para os atacantes quebrarem a hash da senha, garantindo a segurança das senhas mesmo em caso de violação de dados._
-
Salting – O BcryptJS lida com a geração de “salts” aleatórios para senhas, garantindo a segurança do armazenamento (aprenderemos mais detalhes sobre “hashes” e “salts” na próxima seção). “Salts” tornam a hash de uma senha relativamente fraca mais complexa, dificultando sua descriptografia.
-
Fácil de Usar – O BcryptJS fornece aos desenvolvedores JavaScript uma ferramenta para criptografar suas senhas sem exigir um profundo entendimento de hash._
Na próxima etapa, aprenderemos brevemente sobre hashes e salts.
Como funciona o Hashing?
Antes de usar esses conceitos em seus projetos, é necessário entender como o hashing e o salting funcionam.
Hashing
O hashing consiste em converter uma simples string ou texto em uma sequência de caracteres aleatórios (criptografia). Isso permite o armazenamento seguro e/ou transmissão de dados sensíveis. O hashing envolve as seguintes etapas principais:
Entrada de dados – Primeiramente, os dados, que podem ser de qualquer tipo (binário, caractere, decimal, etc.) em formato de texto simples ou string, são armazenados.
A função de hash – A função de hash é um algoritmo matemático que recebe a entrada dos dados e os converte em um conjunto de caracteres ou códigos de hash. As funções de hash são determinísticas (produzem a mesma saída para a mesma entrada) e unidirecionais (isso significa que é quase impossível engenharia reversa a saída das funções de hash, ou seja, um hash em seus dados de entrada).
Resistência à colisão – Isso significa que uma função de hash é criada tendo em mente a ideia de resistência à colisão, ou seja, duas entradas diferentes não podem ter a mesma saída (código de hash).
Autenticação – As funções de hash são determinísticas, produzindo o mesmo hash para a mesma entrada. Assim, ao autenticar uma senha armazenada como um hash, a ideia geral é que se a senha a ser autenticada corresponder ao hash armazenado no banco de dados, a senha está correta.
Salt
Como o hashing existe há décadas, houve desenvolvimentos como tabelas rainbow, que contêm bilhões de entradas de dados contendo strings de dados e seus respectivos hashes com base em diferentes algoritmos de hashing.
Agora, considere uma situação em que um usuário cria uma conta em seu site com uma senha fraca. Assim, em caso de violação de dados, um atacante pode procurar os hashes de seus usuários e encontrar a correspondência do hash para a conta do usuário com uma senha fraca. Isso seria desastroso em aplicações de alta segurança. Para evitar que isso ocorra, são usados salts.
O sal é uma camada adicional de segurança adicionada aos hashes ao adicionar uma sequência de caracteres aleatória ao hash de uma senha antes de armazená-lo em um banco de dados. Assim, mesmo que os dados sejam vazados em uma violação, será difícil para um atacante descriptografar um hash contendo sal. Considere o seguinte exemplo:
Como podemos ver claramente, a senha armazenada com sal é menos provável de ser quebrada devido à natureza determinística dos hashes. Portanto, se um atacante, por exemplo, procurar essa string de hash+senha em uma tabela arco-íris, ele não obterá a senha real, mas algo completamente diferente.
Agora, você está pronto para usar o BcryptJS e proteger suas senhas de acordo com os padrões da indústria.
Instalando o BcryptJS e outros módulos necessários
Agora que você conhece o hashing e o sal, tudo o que resta é pegar seu computador e começar a codificar. A estrutura do projeto será a seguinte:

Primeiro, vamos começar criando um projeto npm com as seguintes etapas:
-
Abra uma pasta e crie um arquivo chamado
app.js.
-
Abra uma janela do terminal nesta pasta e digite o comando
Depois disso, será solicitado que você insira algumas informações, mas você pode pressionar Enter sem fornecer nenhuma informação.
- Em seguida, crie mais 3 arquivos, sendo eles
auth.js
db.js
User.js
- No mesmo terminal, digite o seguinte comando para instalar os pacotes necessários.
Agora você possui um ambiente de projeto completo para seguir neste tutorial. No próximo passo, você aprenderá como criar um servidor para usar o BcryptJS para armazenar e autenticar senhas com segurança no MongoDB.
Configurando um servidor com o Express JS
Agora que você configurou a estrutura do projeto, pode criar um servidor que usa o bcryptjs
para proteger as senhas armazenando-as como hashes e autenticando-as. Vamos criar o servidor seguindo as etapas corretas a seguir.
Etapa 1 – Criando uma conexão com o banco de dados MongoDB
Para conectar ao MongoDB
, estamos usando a edição da comunidade. Para manter o projeto organizado, você salvará o código para configurar uma conexão no arquivo db.js
.
Aqui, você importa o pacote mongoose
, que fornece uma API para conectar o JavaScript ao MongoDB.
Além disso, neste caso, a URI de conexão é para uma instalação local do MongoDB. Se você estiver usando um banco de dados em nuvem (como o Atlas), basta alterar a URI para a URI específica do seu banco de dados.
Informação
Uma URI é semelhante a um URL de servidor com a diferença de que uma URI pode identificar o nome e a identidade dos recursos e sua localização na internet. Em contraste, um URL é um subconjunto de uma URI capaz de fazer apenas o último.
A função connectToMongo
é uma função async porque mongoose.connect
retorna uma promessa javascript. Esta função fornecerá uma conexão em caso de execução bem-sucedida. Caso contrário, retornará um erro.
Por fim, usamos module.exports
para exportar essa função sempre que o módulo db.js
for importado.
Passo 2 – Criando um Esquema de Usuário
Você precisará de um esquema básico para criar ou autenticar usuários com um banco de dados MongoDB. Se você não sabe o que é um esquema, pode usar este excelente guia da DO para entender e criar o esquema no MongoDB. Usaremos apenas dois campos para nosso esquema, email
e password
.
Use o código a seguir em seu módulo User.js
.
Criamos dois campos neste esquema, email
e password
. Ambos são campos obrigatórios e do tipo de dado string. Além disso, o campo email
é único, o que significa que um email pode ser usado apenas uma vez para ter uma conta neste servidor.
Finalmente, exportamos um modelo chamado users
. Um modelo representa um esquema no banco de dados. Você pode pensar em um esquema como uma regra para definir um modelo, enquanto o modelo é armazenado como uma coleção no banco de dados MongoDB
.
Você pode converter um esquema em um modelo usando a função model() da biblioteca mongoose
.
Passo 3 – Configurando o servidor em app.js
Após seguir os passos anteriores, você criou com sucesso um modelo e um módulo para fazer uma conexão com o banco de dados MongoDB
. Agora, você aprenderá a configurar o servidor. Use o seguinte código no seu arquivo app.js
.
Aqui, importamos os módulos express
e db.js
para conectar ao MongoDB. Em seguida, usamos o middleware express.json()
para lidar com respostas JSON. As rotas são criadas em um módulo diferente (auth.js
) para manter o código limpo e organizado. Por fim, criamos um endpoint para o servidor ouvir na porta 3300
em localhost. (Você pode usar qualquer porta de sua escolha)
Criptografando Senhas e Armazenando-as em um Banco de Dados MongoDB
Até este ponto, o servidor está quase pronto, e agora você irá criar endpoints para o servidor. Vamos criar dois endpoints – signup
e login
. No endpoint de signup, iremos pegar o email e a senha de um novo usuário e armazená-los com criptografia para a senha usando BcryptJS.
No arquivo auth.js
, digite o seguinte código:
Fazemos as importações necessárias e em seguida configuramos o router express para criar o endpoint /signup
. Estamos utilizando o método POST
para que as credenciais não sejam reveladas na URL da aplicação. Após isso, criamos um Salt utilizando a função genSalt
do pacote scripts
; o parâmetro passado para a função genSalt() contém o tamanho dos caracteres do salt. Em seguida, utilizamos a função hash() do BcryptJS, que recebe um parâmetro obrigatório, a string de senha a ser convertida em código de hash, e um argumento opcional, a string de salt. E então ela retorna um hash que contém tanto a senha
quanto o salt
.
Depois disso, usamos a função create() do módulo mongoose
para criar um documento no nosso banco de dados definido pelas regras do modelo users. Ela recebe um objeto Javascript
contendo email e senha, mas ao invés de passar uma string bruta, passamos o secPass (hash da senha + salt) para ser armazenado no banco de dados. Dessa forma, armazenamos de forma segura uma senha no banco de dados utilizando seu hash combinado com um salt ao invés de uma string bruta. Por fim, retornamos uma resposta JSON
contendo o modelo do usuário. (Esse método de envio de respostas é apenas para a fase de desenvolvimento
; em produção, você substituirá isso por um token de autenticação ou algo similar).
Para testar esse endpoint, você precisa rodar o servidor primeiro, o que pode ser feito digitando o seguinte comando no terminal.
Esse comando irá rodar o seu servidor em localhost e na porta 3300
(ou em qualquer outra porta que você especificar). Em seguida, você pode enviar uma requisição HTTP
para a URL http://localhost:3300/auth/signup
com o seguinte corpo:
Isso irá produzir a seguinte saída/resposta:
Observação: O hash da senha e o ID não serão os mesmos, já que eles são sempre únicos.
Na próxima seção, você aprenderá como acessar o hash armazenado para a senha e autenticá-lo com uma senha fornecida durante o login/autenticação.
Acessando a senha criptografada e usando-a para autenticação
Até agora, você aprendeu sobre BcryptJS,
hashing,
salting,
e desenvolveu um servidor express
que cria novos usuários com senhas armazenadas como hashes. Agora, você aprenderá como usar a senha armazenada e autenticar um usuário quando ele tentar fazer login na aplicação.
Para adicionar um método de autenticação, adicione a seguinte rota em seu arquivo auth.js
após a rota /signup
:
Aqui, usamos o subcaminho /auth/login
para realizar a autenticação de login para um usuário já existente. Assim como no endpoint /auth/signup
, essa será uma função async-await, já que o BcryptJS retorna promessas.
Primeiramente, usamos a função findOne
da biblioteca Mongoose, que é usada para encontrar um documento em uma coleção com base em uma determinada consulta de pesquisa. Neste caso, estamos procurando o usuário com base no email. Se nenhum usuário com o email fornecido existir, isso enviará uma resposta com o código de status 400 para credenciais inválidas. (Não é uma boa prática fornecer qual parâmetro está incorreto durante o login, pois os atacantes podem usar essas informações para encontrar contas existentes).
Se o usuário com o email fornecido existe, o programa passa para a comparação de senhas. Para esse fim, o BcryptJS
fornece o método compare()
, que recebe uma string bruta como primeiro argumento e um hash
(com ou sem salt
) como segundo argumento. Em seguida, ele retorna uma promessa booleana
; true
se a senha corresponder ao hash e false se não corresponderem. Em seguida, você pode adicionar uma verificação simples usando uma declaração if e retornar sucesso ou erro com base na comparação.
Finalmente, você exportará o express router
usando module.exports para que o ponto de partida app.js
possa usá-lo para rotas.
Para testar esta rota, você pode enviar outra resposta HTTP
para esta URL http://localhost:3300/auth/login
com o corpo como segue:
Esta solicitação fornecerá a seguinte resposta:
Finalmente, o auth.js
ficará assim:
Conclusão
Este tutorial criou um servidor para explicar o uso do BcryptJS para armazenar e acessar com segurança as senhas do banco de dados. Você pode levar isso adiante:
-
Implementando mais rotas para outras tarefas, como buscar credenciais do usuário, atualizar credenciais do usuário, etc.
-
Implementando tokens de autenticação para enviar como respostas, etc.
Isso inicia sua jornada para lidar com senhas de forma segura; você sempre pode adicionar mais segurança com diferentes técnicas e tecnologias, permitindo que você crie aplicativos mais seguros e resilientes.