wedding-management-system

🔐 Segurança

Visão geral das proteções implementadas e práticas recomendadas para deploy.

Autenticação

Revalidação periódica de sessão JWT

O JWT é revalidado contra o banco a cada 60 segundos no callback jwt em src/auth.ts. A cada hit que passa pela função auth() em server-side, se a última checagem foi há mais de 1 min, o callback consulta o banco e atualiza no token:

O middleware (src/auth.config.ts) lê session.user.isActive e session.user.archived e deslogará (redirect para /login?revoked=1) qualquer usuário desativado/arquivado em até 1 min após a alteração. Sem isso, JWTs em curso continuariam válidos até a expiração natural.

Senha provisória + troca obrigatória

Reset de senha

2FA (TOTP)

Verificação correta da resposta

const isValid = verifyTotpToken(token, secret);  // retorna boolean

A versão atual do helper já abstrai a API do otplib. Não chame verify direto — passe sempre por src/lib/totp.ts.

Autorização — Server Actions e endpoints sensíveis

Em Next.js App Router, toda função exportada de um arquivo "use server" é um endpoint HTTP público. Não basta esconder a action no frontend — um atacante autenticado (ou nem isso) pode invocá-la diretamente via POST.

Use sempre um dos helpers em src/lib/finance-access.ts como primeira instrução de cada Server Action:

import { denyIfNoEdit } from "@/lib/finance-access";

export async function updateGuest(_state, formData) {
  const denied = await denyIfNoEdit();
  if (denied) return denied;
  // ...
}
Helper Permite Use para
denyIfNoEdit() ADMIN, GROOM, BRIDE, PLANNER Conteúdo operacional do casamento (convidados, fornecedores, tarefas, lua de mel, enxoval, dia D).
denyIfNoFinance() ADMIN, GROOM, BRIDE Pagamentos, receitas, metas, ativos — qualquer mutação financeira direta.
denyIfNoManage() ADMIN, GROOM, BRIDE Configuração do evento (data, moeda, dados Pix, nomes do casal).

Exceções (ações públicas por design): requestPasswordReset, consumePasswordReset, validateResetToken, publicRsvpRespond. Estas precisam de rate-limit no lugar do auth check.

Endpoints REST que servem dados financeiros (/api/backup, /api/files/[id]) checam role via canViewSensitiveFinance() direto — qualquer autenticado que não esteja nesse grupo recebe 403. Todo download via /api/backup grava AuditLog (BACKUP_EXPORT) para rastreabilidade.

Comparação de secrets — timing-safe

Toda comparação de Bearer token, HMAC, código de reset (quando comparado em JS), e similares deve usar timingSafeEquals(a, b) de src/lib/timing-safe.ts. Nunca ===.

Cron jobs

/api/cron/reminders exige Authorization: Bearer ${CRON_SECRET}. A comparação é timing-safe. Gere o secret com openssl rand -hex 32 e nunca exponha sem auth.

Rate limiting

Módulo em src/lib/rate-limit.ts — in-memory, adequado para deploy single-node. Use em:

Chave deve combinar IP + recurso. Exemplo:

const ip = getClientIp(req);
const rl = rateLimit(`login:${ip}`, 10, 60_000);
if (!rl.ok) {
  return Response.json({ error: "Calma" }, { status: 429 });
}

O limiter faz eviction periódica (sweep a cada 60 s) de buckets expirados — o Map interno não cresce indefinidamente, mesmo sob spray de IPs únicos.

getClientIp(headers) aceita apenas cf-connecting-ip (Cloudflare) e o último hop de x-forwarded-for. Não confiamos em x-real-ip porque qualquer cliente pode forjar o header quando não há proxy na frente.

⚠️ Para deploys com múltiplas réplicas, troque o limiter por Redis.

Audit log

Cada ação relevante grava em AuditLog:

await audit("Payment", payment.id, "MARK_PAID", { method: "PIX" });

Campos: entity, entityId, action, payload (JSON serializado), userId, createdAt.

Trilha visível na UI

A trilha é exibida em Configurações › Auditoria (/dashboard/settings), aba restrita a quem tem canManageUsers (ADMIN/PLANNER). Mostra as últimas 500 ações com filtros por entidade, ação, intervalo de data e busca textual (ID da entidade ou pessoa), além de paginação. O userId é resolvido para nome/e-mail no servidor; ações sem ator (cron/sistema) aparecem como “Sistema”. O payload não é exposto na UI — pode conter dados sensíveis, então a tela fica em quem/o-quê/quando.

Auto-captura do userId

O 5º argumento (userId) é opcional. Quando omitido, audit() faz um import dinâmico de @/auth, chama auth() e extrai session.user.id. Falhas (cron sem request lifecycle, scripts standalone) são silenciadas e o registro fica com userId = null. Isso elimina o boilerplate antigo em que cada call-site precisava propagar session.user.id manualmente — e o esquecimento gerava trilha cega.

Entities cobertas

AuditEntity em src/lib/audit.ts cobre Vendor, VendorContact, VendorNote, BudgetItem, Payment, Asset, Income, SavingsGoal, EventSettings, User, SecuritySettings, SeatingTable, Guest, GuestGroup, Gift, Contract, Attachment, Task, Venue, Honeymoon, HoneymoonItem, TrousseauItem. Ao adicionar um novo modelo Prisma com mutação auditada, estenda essa union antes de chamar audit() — TypeScript pega o erro.

Actions auditadas

Mutações em todos os modelos passam por audit(): create, update, delete, status change, bulk import, assign/unassign de mesa, RSVP em grupo, 2FA enable/disable, backup export, upload/download/replace/sign de anexo, reset de senha e mudança de própria senha.

Validação de entrada

Escape de HTML em emails

Templates de email são renderizados como HTML — dados de usuário devem passar por escape. Existe um helper padrão (se ainda não, considere extrair um escapeHtml(value) em src/lib/html-escape.ts).

Upload de arquivos

/api/files/[id] aceita uploads de anexos (contratos, fotos de venues, etc.). Boas práticas:

Recomendações de produção

Item Recomendação
HTTPS Obrigatório. Cloudflare Tunnel ou nginx + Let’s Encrypt.
Cookies Secure + HttpOnly (Auth.js já configura quando NEXTAUTH_URL é https)
AUTH_TRUST_HOST true quando atrás de proxy/CDN.
Headers de segurança Configurar Content-Security-Policy, Strict-Transport-Security, X-Frame-Options: DENY no proxy ou em next.config.ts.
Banco Manter dev.db em diretório persistente (não em pasta volátil). Backup diário.
.env NUNCA versione. Use sua plataforma para injetar.
ADMIN_PASSWORD Trocar imediatamente no primeiro login (o sistema força).
Atualizações npm audit periódico, especialmente em libs de auth e crypto.

LGPD / Privacidade

O sistema armazena dados pessoais (nomes, telefones, emails de convidados, fornecedores, parceiros). Quem opera o sistema é o responsável pela LGPD — recomendações:

Upload de anexos e contratos (v0.4.0)

Endurecimento aplicado ao módulo de anexos — veja anexos.md para detalhes completos. Resumo das proteções:

Checklist do reviewer

Ao revisar PR que toque endpoints/Server Actions, conferir: