Armazenamento seguro de credenciais e tokens no cliente e no servidor

Capítulo 8

Tempo estimado de leitura: 9 minutos

+ Exercício

O que significa “armazenamento seguro” de credenciais e tokens

Credenciais (como senhas) e tokens (access/refresh tokens, chaves de API, segredos de assinatura) são ativos de alto impacto: se vazarem, permitem acesso indevido mesmo sem explorar falhas complexas. Armazenamento seguro envolve três frentes: (1) persistência (como salvar no banco/cliente), (2) trânsito e exposição (logs, erros, observabilidade), e (3) gestão de segredos (variáveis de ambiente, rotação e controle de acesso).

Senhas no servidor: nunca armazenar “como veio”

Hashing com sal e custo apropriado

Senhas devem ser armazenadas como hash usando algoritmos próprios para senha (lentos e resistentes a força bruta), com sal único por usuário e parâmetros de custo ajustados. Opções comuns:

  • Argon2id: recomendado na maioria dos cenários modernos (resistente a GPU/ASIC por ser memory-hard).
  • bcrypt: amplamente suportado e seguro quando bem parametrizado (custo/work factor adequado).
  • scrypt: também memory-hard, boa alternativa.

Boas práticas:

  • Gerar sal aleatório por senha (o algoritmo normalmente já embute o sal no hash final).
  • Definir custo para que o hash leve algo como 100–300ms no seu hardware de produção (ajuste com benchmark).
  • Armazenar no banco apenas a string do hash (que inclui sal e parâmetros), nunca a senha.
  • Usar comparação em tempo constante fornecida pela biblioteca (evita timing attacks).

Passo a passo prático: cadastro e login com Argon2id (Node.js)

Exemplo com argon2 (conceito aplicável a outras linguagens):

// npm i argon2
import argon2 from "argon2";

// 1) Cadastro: gerar hash e salvar
export async function hashPassword(plainPassword) {
  return argon2.hash(plainPassword, {
    type: argon2.argon2id,
    memoryCost: 19456, // ~19 MB (ajuste por benchmark)
    timeCost: 2,
    parallelism: 1,
  });
}

// 2) Login: verificar senha
export async function verifyPassword(storedHash, plainPassword) {
  return argon2.verify(storedHash, plainPassword);
}

Notas importantes:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Os valores de memoryCost/timeCost são exemplos. Meça em produção e ajuste para equilibrar segurança e latência.
  • Se você mudar parâmetros no futuro, muitas bibliotecas permitem rehash ao autenticar (quando detectam que o hash antigo está “fraco”).

Passo a passo prático: cadastro e login com bcrypt (Node.js)

// npm i bcrypt
import bcrypt from "bcrypt";

const COST = 12; // ajuste por benchmark

export async function hashPassword(plainPassword) {
  const salt = await bcrypt.genSalt(COST);
  return bcrypt.hash(plainPassword, salt);
}

export async function verifyPassword(storedHash, plainPassword) {
  return bcrypt.compare(plainPassword, storedHash);
}

O que não fazer com senhas

  • Não usar sha256(password), md5 ou hashes rápidos: são inadequados para senha.
  • Não “criptografar e salvar”: se a chave vazar, todas as senhas vazam. Hash é o padrão para senhas.
  • Não reutilizar sal global para todos os usuários.
  • Não logar senha (nem parcialmente) e não retornar em erros.

Tokens e segredos no servidor: reduzir exposição e controlar acesso

Nunca logar segredos (tokens, senhas, chaves, cookies)

Logs são frequentemente enviados para sistemas externos (observabilidade, SIEM) e acessados por muitas pessoas. Um token em log pode virar acesso indevido. A regra prática é: logar metadados, não segredos.

O que evitar em logs:

  • Authorization: Bearer ...
  • Cookie: ... (principalmente cookies de sessão e refresh)
  • Corpos de requisição com password, refresh_token, client_secret
  • Headers de integração (X-API-Key)

Passo a passo prático: redaction/máscara em logs

Exemplo genérico de sanitização antes de logar requisições (Node/Express):

function redact(value) {
  if (!value) return value;
  // Mantém só prefixo/sufixo para correlação sem vazar o segredo
  const str = String(value);
  if (str.length <= 10) return "[REDACTED]";
  return `${str.slice(0, 6)}...${str.slice(-4)}`;
}

function sanitizeHeaders(headers) {
  const h = { ...headers };
  if (h.authorization) h.authorization = "Bearer [REDACTED]";
  if (h.cookie) h.cookie = "[REDACTED]";
  if (h["x-api-key"]) h["x-api-key"] = redact(h["x-api-key"]);
  return h;
}

function sanitizeBody(body) {
  if (!body || typeof body !== "object") return body;
  const b = { ...body };
  const secretFields = ["password", "pass", "token", "refresh_token", "client_secret"];
  for (const key of secretFields) {
    if (key in b) b[key] = "[REDACTED]";
  }
  return b;
}

// Exemplo de uso em middleware
export function requestLogger(req, _res, next) {
  const safe = {
    method: req.method,
    path: req.path,
    headers: sanitizeHeaders(req.headers),
    body: sanitizeBody(req.body),
  };
  console.info("request", safe);
  next();
}

Diretriz: se você precisa depurar algo sensível, prefira logs temporários em ambiente controlado, com retenção curta e acesso restrito, e remova após o diagnóstico.

Proteger variáveis de ambiente e segredos (conceito de vault/secret manager)

Variáveis de ambiente são um mecanismo de injeção de configuração, mas não são automaticamente “seguras”. O objetivo é garantir que segredos:

  • Não sejam commitados em repositório.
  • Não apareçam em logs, dumps, páginas de erro, ou ferramentas de debug.
  • Tenham acesso mínimo (princípio do menor privilégio).
  • Possam ser rotacionados sem grandes mudanças.

Conceito de Secret Manager/Vault:

  • Um serviço central que armazena segredos criptografados, com controle de acesso, auditoria e rotação.
  • A aplicação obtém segredos em runtime via identidade (ex.: role/service account), evitando arquivos locais com chaves.
  • Permite rotação e revogação sem redeploy completo (dependendo do desenho).

Passo a passo prático: checklist de gestão de segredos no servidor

  • 1) Inventariar segredos: chaves JWT, chaves de criptografia, senhas de banco, API keys, client secrets.
  • 2) Definir fonte: Secret Manager/Vault (preferível) ou variáveis de ambiente injetadas pelo runtime.
  • 3) Restringir acesso: apenas o serviço que precisa deve ler o segredo.
  • 4) Rotação: planejar rotação periódica (ex.: trimestral) e rotação emergencial (incidente).
  • 5) Auditoria: registrar quem acessou/alterou segredos (no sistema de segredos, não na aplicação).
  • 6) Evitar exposição: nunca imprimir process.env inteiro, nem retornar segredos em endpoints de debug.

Armazenamento de tokens no cliente (Web): escolhas e riscos

Em aplicações web, o armazenamento do token impacta diretamente a superfície de ataque. O ponto central é entender dois riscos principais:

  • XSS: se um atacante executa JavaScript no seu site, ele pode ler dados acessíveis ao JS (como localStorage) e exfiltrar tokens.
  • CSRF: se o navegador envia credenciais automaticamente (como cookies), um site malicioso pode induzir o navegador a fazer requisições autenticadas.

localStorage/sessionStorage: prático, mas sensível a XSS

Vantagens:

  • Fácil de implementar e debugar.
  • O token pode ser enviado manualmente no header Authorization.

Riscos:

  • Qualquer XSS bem-sucedido pode ler e roubar o token.
  • Extensões do navegador e scripts injetados podem aumentar o risco.

Quando aparece: SPAs que guardam access token em localStorage e o reutilizam por longos períodos.

Cookies HttpOnly: reduz roubo por XSS, mas exige mitigação de CSRF

HttpOnly impede leitura do cookie via JavaScript, reduzindo o impacto de XSS no roubo direto do token. Porém, cookies são enviados automaticamente pelo navegador, o que traz o risco de CSRF se não houver proteções.

Benefícios:

  • Token/cookie não fica acessível ao JS (mitiga exfiltração via XSS).
  • Integra bem com fluxos baseados em cookie.

Trade-offs:

  • Você precisa lidar com CSRF (especialmente para requisições que mudam estado).
  • Configurações de SameSite, Secure e domínio/path precisam ser corretas.

Implicações práticas de CSRF ao usar cookies

Se a autenticação depende de cookie, o navegador pode enviar esse cookie em requisições iniciadas por outros sites. Mitigações comuns (podem ser combinadas):

  • SameSite: Lax reduz CSRF em muitos cenários; Strict é mais restritivo; None exige Secure e aumenta exposição a CSRF se não houver token anti-CSRF.
  • Token anti-CSRF: um valor adicional (não HttpOnly) enviado em header (ex.: X-CSRF-Token) e validado no servidor.
  • Verificação de Origin/Referer: rejeitar requisições sensíveis sem Origin esperado (útil como camada extra).

Regra prática: cookies HttpOnly tendem a ser preferíveis para reduzir roubo por XSS, desde que você implemente mitigação de CSRF e configure corretamente SameSite/Secure.

Exemplo de configuração de cookie segura (servidor)

// Exemplo conceitual (Express)
res.cookie("access_token", token, {
  httpOnly: true,
  secure: true,        // apenas HTTPS
  sameSite: "lax",     // ajuste conforme seu fluxo
  path: "/",
  maxAge: 15 * 60 * 1000,
});

Pontos de atenção:

  • secure: true deve ser obrigatório em produção (HTTPS).
  • sameSite depende do seu fluxo (ex.: integrações cross-site podem exigir None + anti-CSRF).
  • Evite cookies com escopo amplo desnecessário (domínio/path).

Diretrizes de configuração por ambiente (dev/staging/prod)

Ambientes diferentes exigem controles diferentes, mas sem “abrir mão” de princípios básicos. A ideia é: em dev você pode facilitar observabilidade, mas não deve normalizar práticas inseguras (como logar tokens).

Tabela de recomendações por ambiente

ItemDevStagingProd
HTTPSPreferível (ou proxy local)ObrigatórioObrigatório
Cookie SecurePode ser false se sem HTTPS localtruetrue
SameSiteLax (ou conforme testes)Lax/Strict conforme fluxoLax/Strict; None só com necessidade + anti-CSRF
Logs de request/responseMais verbosos, mas com redactionRedaction obrigatórioRedaction obrigatório + mínimo necessário
Segredos.env local (não commitado)Secret manager (ideal)Secret manager/vault + rotação
Rotação de chavesManual quando necessárioPlanejada e testadaPeriódica + processo de incidente

Passo a passo prático: padrão de configuração segura

  • 1) Separar config de código: nada de chaves no repositório; use variáveis/secret manager.
  • 2) Validar config ao subir: falhar rápido se faltar segredo obrigatório.
  • 3) Redaction centralizado: um único utilitário/middleware para mascarar segredos em logs.
  • 4) Diferenciar níveis de log: debug só em dev; em prod, info/warn/error com payload mínimo.
  • 5) Retenção e acesso: logs com retenção adequada e acesso restrito (principalmente em staging/prod).

Checklist rápido: evitar vazamento de credenciais e tokens

  • Senha: usar Argon2id/bcrypt/scrypt com custo calibrado; nunca armazenar em texto; nunca logar.
  • Tokens/segredos: não logar headers/cookies; aplicar redaction em logs e erros.
  • Segredos: preferir secret manager/vault; restringir acesso; auditar; rotacionar.
  • Cliente web: evitar armazenar tokens acessíveis ao JS quando possível; considerar cookies HttpOnly com mitigação de CSRF.
  • Ambientes: dev/staging/prod com políticas claras (HTTPS, cookies, logs, segredos) e sem “atalhos” perigosos.

Agora responda o exercício sobre o conteúdo:

Ao escolher onde armazenar tokens de autenticação em uma aplicação web, qual abordagem reduz o risco de roubo do token via XSS, mas exige mitigação de CSRF por o navegador enviar a credencial automaticamente?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Cookies HttpOnly não podem ser lidos por JavaScript, reduzindo a exfiltração do token em caso de XSS. Porém, como cookies são enviados automaticamente pelo navegador, é preciso mitigar CSRF (ex.: SameSite e/ou token anti-CSRF).

Próximo capitúlo

Proteções contra ataques comuns em autenticação e autorização (CSRF, XSS, brute force)

Arrow Right Icon
Capa do Ebook gratuito Autenticação e Autorização no Back-end: Sessões, JWT e Boas Práticas
44%

Autenticação e Autorização no Back-end: Sessões, JWT e Boas Práticas

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.