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:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Os valores de
memoryCost/timeCostsã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),md5ou 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.envinteiro, 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:
Laxreduz CSRF em muitos cenários;Stricté mais restritivo;NoneexigeSecuree 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
Originesperado (ú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: truedeve ser obrigatório em produção (HTTPS).sameSitedepende do seu fluxo (ex.: integrações cross-site podem exigirNone+ 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
| Item | Dev | Staging | Prod |
|---|---|---|---|
| HTTPS | Preferível (ou proxy local) | Obrigatório | Obrigatório |
| Cookie Secure | Pode ser false se sem HTTPS local | true | true |
| SameSite | Lax (ou conforme testes) | Lax/Strict conforme fluxo | Lax/Strict; None só com necessidade + anti-CSRF |
| Logs de request/response | Mais verbosos, mas com redaction | Redaction obrigatório | Redaction obrigatório + mínimo necessário |
| Segredos | .env local (não commitado) | Secret manager (ideal) | Secret manager/vault + rotação |
| Rotação de chaves | Manual quando necessário | Planejada e testada | Perió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:
debugsó em dev; em prod,info/warn/errorcom 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.