Segurança

O que está implementado

Autenticação

  • PKCE obrigatóriocode_challenge exigido em todos os authorization requests
  • Argon2id — senhas hasheadas com memoryCost=19456, timeCost=2
  • MFA (TOTP) — compatível com Google Authenticator, Authy
  • Email OTP — verificação por código de 6 dígitos (TTL 10 min, máx 5 tentativas)
  • Google OAuth — integração via Passport.js, verifica membership antes de completar login

Tokens

  • RS256 — tokens assinados com chave RSA-2048 privada
  • JTI revogation — tokens revogados ficam no Redis até expirar
  • TTL curto — access_token expira em 10 minutos
  • Refresh token rotation — cada uso gera um novo refresh_token (reuse = revogação automática)

Proteção contra ataques

  • Rate limiting por IP: 20 req/60s globais em /interaction/*
  • Rate limiting por email: 10 falhas/5min → lockout; 20 falhas/60min → lockout longo
  • Rate limiting password reset: 3 req/email/hora
  • Rate limiting email OTP: limitado por IP
  • Session fixation — oidc-provider cria nova sessão a cada fluxo
  • HMAC-SHA256 em webhooks com tolerância de 5 minutos (anti-replay)

Headers HTTP

  • Helmet.js — X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
  • HSTS — em produção (max-age=31536000; includeSubDomains; preload)
  • CSP — ativo em produção
  • SameSite=Lax — cookies da sessão OIDC

Admin

  • AdminGuard — verifica JWT + not-revoked + is_admin=true + MFA
  • SystemScoped — system-admins só acessam seus próprios sistemas
  • Audit log — todas as ações POST/PATCH/DELETE em /admin/* são registradas

Checklist para produção

Secrets — rotacionar antes do primeiro deploy

# Gerar JWKS key
node -e "
const jose = require('jose');
jose.generateKeyPair('RS256').then(async ({privateKey}) => {
  const k = await jose.exportJWK(privateKey);
  k.kid = 'sig-1'; k.alg = 'RS256'; k.use = 'sig';
  console.log('JWKS_PRIVATE_KEYS=' + JSON.stringify([k]));
});
"

# Cookie keys
node -e "const c=require('crypto'); console.log('COOKIE_KEYS='+c.randomBytes(32).toString('hex')+','+c.randomBytes(32).toString('hex'))"

# Webhook secret
node -e "console.log('WEBHOOK_HMAC_SECRET='+require('crypto').randomBytes(32).toString('hex'))"

Variáveis obrigatórias em produção

NODE_ENV=production
ISSUER=https://auth.seudominio.com
ADMIN_CONSOLE_URL=https://admin.seudominio.com
CORS_ORIGINS=https://admin.seudominio.com,https://app.seudominio.com
MFA_REQUIRED=true
ADMIN_PASSWORD=<senha forte, min 12 chars>

Infraestrutura

  • HTTPS — certificado TLS em todos os domínios
  • Reverse proxy com trust proxy = 1 (nginx/ALB injetando X-Forwarded-For)
  • Redis persistente — AOF habilitado para não perder sessões
  • PostgreSQL com backups automáticos diários
  • Logs coletados (CloudWatch, Datadog, Loki)
  • Alertas em erros 5xx e rate limit spikes

Pós-deploy

  • Rodar test-carteira-demo.mjs contra produção para validar o fluxo completo
  • Verificar /.well-known/openid-configuration retorna https:// em todos os campos
  • Verificar que botão Google não aparece na tela de login do admin-console (se não quiser)
  • Testar logout e verificar que refresh_token é revogado