Menu
CoddyTech

Nunca armazene senhas

Parte da seção Autenticação e segurança do Journey de Node.js da Coddy. Lição 4 de 24.

As pessoas reutilizam senhas, então uma senha em texto simples que vaza pode expor contas em outros lugares. Armazene um hash de senha com salt: um valor derivado que você verifica recalculando-o, em vez de decodificá-lo.

const salt = randomBytes(16).toString('hex');
const key = await scryptAsync(inviteCode, salt, 32);
const saved = salt + '.' + key.toString('hex');

Este exemplo combina um salt e uma chave derivada para um código de convite. O delimitador permite que a verificação recupere o salt e derive outra chave com os mesmos parâmetros. Sua tarefa de senha usa seu próprio delimitador e comprimento de chave. Gere o salt para cada nova senha, derive a chave e, em seguida, armazene ambos. Hashes com salt ainda exigem senhas fortes, pois um invasor pode tentar adivinhá-las.

challenge icon

Desafio

Fácil

No momento, o cadastro salva cada senha exatamente como foi digitada. Faça com que ele salve um hash com salt, para que um vazamento do banco de dados não revele as senhas.

  1. Em passwords.js, altere hashPassword(password) para que retorne o texto que o cadastro salva, no formato salt, dois-pontos, hash:
    • O salt tem 16 bytes aleatórios, gerados novamente a cada chamada e escritos em hexadecimal: 32 caracteres de 0 a 9 e de a a f.
    • O hash é uma chave de 64 bytes que scrypt cria a partir da senha e desse salt, também escrita em hexadecimal: 128 caracteres. O terceiro argumento de scryptAsync é o comprimento da chave em bytes.
    • Junte os dois com dois-pontos. Um valor salvo fica assim: f2bba0669d9fa2d203afc7e61bf8f065:c3f7cae352e023a8d4766da9a316b5a2c844100efff22c4079f89a8ab8512ac91c33227481c3afa88c3ba0e88347f9e3e0c22dad9d55f19688a350f71be8dbd2
  2. Você não precisa alterar server.js. O POST /signup já chama hashPassword e salva o que ela retorna. Ele também tem GET /debug/users/:username, que mostra o que foi salvo para um usuário. Essa rota existe apenas para você conferir; um aplicativo real nunca tem essa rota.

Experimente: Pressione Executar. Envie POST /signup com {"username": "ada", "password": "correct horse"}. Você recebe o status 201 e {"username": "ada"}. Envie o mesmo corpo com "username": "bob". Agora envie GET /debug/users/ada e, em seguida, GET /debug/users/bob. Cada passwordHash tem 32 caracteres, dois-pontos e mais 128 caracteres. Os dois valores são diferentes, embora ada e bob tenham escolhido a mesma senha.

Experimente você mesmo

import { randomBytes, scrypt } from 'node:crypto';
import { promisify } from 'node:util';

const scryptAsync = promisify(scrypt);

// Converte uma senha no texto que signup armazena.
// Por enquanto, mantém a senha como foi digitada.
export async function hashPassword(password) {
    return password;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Autenticação e segurança

Pratique por conta própria: Playground de Express