Segurança
O modelo de segurança do Covil é de defesa em profundidade: cada operação sensível é protegida em mais de uma camada, de modo que uma falha isolada não vira brecha. Este documento descreve o modelo e as regras que não podem ser violadas.
As duas camadas
- Guarda na aplicação — Server Actions e páginas admin verificam autenticação e autorização (papel + seção) antes de qualquer efeito.
- RLS no Postgres — mesmo que a camada 1 falhe ou seja contornada, as policies do banco barram leitura/escrita indevida. A RLS é o anteparo final.
A lógica crítica (associação, pontos, cupons, carteirinha) fica em funções
SECURITY DEFINER no banco, que validam o papel por dentro — ver
banco-de-dados.md.
Regras invioláveis
1. O cliente nunca é confiável
createCheckoutAction (checkout-actions.ts)
rebusca cada produto/ingresso no banco, reaplica o preço de sócio, revalida o
cupom no servidor (cupom pessoal só vale para o dono) e recomputa o total. O
preço/total vindo do cliente serve só para exibição. Estoque e limite por pedido
de ingresso também são validados no servidor.
2. Toda ação admin tem guarda de papel e seção
Escritas admin passam por getAdminClient/getPrivilegedClient, que chamam
requireSectionAccess (papel tem acesso àquela seção?) antes de tocar o banco.
O mapa SECTION_BY_PATH em admin-actions.ts
é fail-closed: se um path não estiver mapeado, o acesso é negado (não
liberado para "qualquer admin").
3. Service role isolado no servidor
supabase/admin.ts usa a service role key, que
ignora a RLS. Ele tem import 'server-only' e só deve ser usado no webhook e
em escritas de pedido no servidor. Nunca o importe em Client Component — o
next build quebra de propósito se isso acontecer.
4. Webhook de pagamento confirmado por reconsulta autoritativa
- Não há assinatura HMAC nem segredo de webhook. A garantia é a reconsulta
autoritativa da cobrança por
txidna API do Sicoob (getPixCharge): o pedido só virapaidse a cobrança estiverCONCLUIDAna própria API. - Por isso um corpo forjado não confirma pagamento — a fonte da verdade é a API do Sicoob, não o corpo recebido no webhook.
- O processamento é idempotente: se o status efetivo não muda, sai cedo;
earn_points/reverse_purchase_pointsnão duplicam efeito.
5. Segredos nunca vazam para o cliente
Só variáveis NEXT_PUBLIC_* chegam ao browser. Segredos —
SUPABASE_SERVICE_ROLE_KEY, SICOOB_*, GMAIL_*, CARD_SIGNING_SECRET —
não têm esse prefixo e são lidos apenas no servidor
(env.ts). Nunca commite .env.local.
6. Dados sensíveis do perfil são imutáveis pelo cliente
Triggers no banco tornam role, status, points_balance e os campos
membership_* somente-leitura via cliente (guard_*). Alterações passam por
funções SECURITY DEFINER (ex.: assign_membership_card, adjust_points).
7. Ledger de pontos imutável
Saldo de pepitas = soma de loyalty_transactions (append-only). Correção é uma
nova transação (adjust/reversal), nunca UPDATE/DELETE. O
profiles.points_balance é só um cache reconciliável.
8. Verificação pública da carteirinha sem PII
verify_membership_card(token) (executável por anon) devolve apenas nome,
foto, número, status, período e plano — nunca e-mail, telefone ou RA. O código de
autenticidade é um HMAC (membership-card.ts)
reexibido para conferência visual.
Privacidade / open redirect
normalizeNextPathbarra open redirect no login (rejeita//host,/\host).- Reset de senha responde de forma uniforme (anti-enumeração de e-mail).
- Não coloque dados pessoais em URL/query string.
Histórico de auditoria
- 2026-08-07 — auditoria de segurança. Corrigidos no código + migration
20260807120000_security_hardening.sql: remoção de auto-escalada asuper_admin;activate_membership/redeem_coupondeixaram de ser executáveis porpublic; separação deis_admin(); cupons não legíveis poranon; storage com escopo de dono; correção de open redirect. - 2026-08-11 — correções de robustez: validação de estoque/limite de ingresso no checkout (anti-oversell), unificação do cálculo de pepitas no webhook e autorização admin fail-closed.
⚠️ Pendências operacionais: confirme em produção que a migration
20260807120000_security_hardening.sqlfoi aplicada e que os segredos estão configurados (CARD_SIGNING_SECRET,SICOOB_*). Verdeploy.md.