Pular para o conteúdo
Infraestrutura & Tecnologia

Next.js sobre Supabase com segurança

Runbook DevOps para Next.js sobre Supabase: arquitetura, middleware, padrões de autenticação, rate limiting e integração com Claude Code.

Mansoor Ahmed
Mansoor Ahmed
Head of Engineering 14 min de leitura

Depois que o Supabase como plataforma de backend está rodando de forma estável, o verdadeiro stack de aplicação é construído sobre ele.

Em muitos projetos modernos, o Next.js assume o papel da camada de aplicação:

  • Frontend Rendering
  • Server Side Rendering
  • Server Actions
  • Route Handlers
  • API Proxy
  • Session Handling

Com isso, o Next.js se torna efetivamente um gateway de backend entre o navegador e o Supabase.

O erro mais comum é tratar essa camada como “frontend”, quando na verdade ela contém lógica do lado do servidor com altos privilégios.

Este runbook descreve como operar o Next.js com segurança sobre uma plataforma Supabase self-hosted.

Cada seção contém:

  • Implementação
  • Condição verificável
  • Cenário de falha

Resumo - Artigo 2 de 6 da série DevOps Runbook

  • Next.js como container separado isolado do Supabase
  • Chave service_role em apenas um arquivo
  • Middleware com getUser() em cada requisição
  • Verificação de ownership antes de mutações
  • Rate limiting em endpoints de autenticação

Índice da série

Este guia faz parte da nossa série de runbooks DevOps para stacks de aplicações self-hosted.

  1. Supabase Self-Hosting Runbook
  2. Next.js sobre Supabase com segurança - este artigo
  3. Supabase Edge Functions com segurança
  4. Trigger.dev Background Jobs em produção segura
  5. Claude Code como controle de segurança no workflow DevOps
  6. Security Baseline para todo o stack

O artigo 1 descreve a base da plataforma. Este artigo descreve a camada de aplicação acima dela.

Visão geral da arquitetura

Browser (Client)
     |
     | HTTPS
     |
Next.js App Layer
     |
     +-- @supabase/ssr
     |
     +-- service_role client
           (apenas contextos admin isolados)
     |
Supabase Platform Layer
     |
     +-- Kong API Gateway
     +-- GoTrue Auth
     +-- PostgREST API
     +-- Realtime
     |
PostgreSQL Data Layer
     |
     +-- Row Level Security

Regras fundamentais:

Browser    -> comunica apenas com Next.js
Next.js    -> comunica com Supabase
Supabase   -> controla acesso via RLS

Quando o navegador se comunica diretamente com vários serviços de backend, surgem fronteiras de segurança incontroláveis.

Parte A - Decisões de arquitetura

Essas decisões raramente são alteradas e formam a base.

A1 - Operar o Next.js como serviço próprio

Implementação

O Next.js roda como seu próprio container.

services

nextjs-app
supabase-stack
postgres

Exemplo docker-compose:

services:

  nextjs-app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_SUPABASE_URL=http://kong:8000
      - NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
      - SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY}
    networks:
      - internal

O Next.js não pode rodar dentro do stack do Supabase.

Condição verificável

docker ps --format '{{.Names}}'

Resultado esperado:

nextjs-app
supabase-kong
supabase-postgres
supabase-auth

Cenário de falha

Quando o Next.js roda no mesmo container que o Supabase:

  • O espaço de processos é compartilhado
  • Os secrets ficam no mesmo environment
  • Um servidor Next.js comprometido tem acesso direto a todos os serviços de backend

A2 - O navegador comunica apenas com o Next.js

Implementação

O navegador deve ver apenas uma URL pública.

https://app.example.com

Não permitido:

https://app.example.com:8000
https://app.example.com:5432
https://app.example.com:9000

Exemplo de firewall:

iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP
iptables -A INPUT -p tcp --dport 5432 -j DROP
iptables -A INPUT -p tcp --dport 9000 -j DROP

Condição verificável

nmap -p 443,3000,5432,8000,9000 app.example.com

Resultado esperado:

443 open
todos os outros filtered

Cenário de falha

Quando o Supabase Studio está acessível publicamente:

  • Acesso completo ao banco de dados
  • Manipulação de schema
  • Acesso aos Storage Buckets

A3 - Configurar Security Headers

O Next.js funciona como gateway e precisa configurar HTTP Security Headers.

Implementação

next.config.js
const securityHeaders = [
  { key: "X-Frame-Options", value: "DENY" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "camera=(), microphone=()" },
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
  {
    key: "Content-Security-Policy",
    value: "default-src 'self'; img-src 'self' data: blob:; frame-ancestors 'none'"
  }
]

Condição verificável

curl -I https://app.example.com

Headers esperados:

X-Frame-Options
Content-Security-Policy
Strict-Transport-Security

Cenário de falha

Sem CSP:

  • Ataques XSS carregam scripts externos
  • Tokens podem ser exfiltrados

Variáveis de ambiente

VariávelVisibilidadeLocalização permitidaRisco de vazamento
NEXT_PUBLIC_SUPABASE_URLCliente.env, código clienteBaixo (URL pública)
NEXT_PUBLIC_SUPABASE_ANON_KEYCliente.env, código clienteMédio (limitado pelo RLS)
SUPABASE_SERVICE_ROLE_KEYApenas servidorlib/supabase/admin.tsCrítico (ignora RLS)
DATABASE_URLApenas servidor.env (servidor)Crítico (acesso direto ao DB)
TRIGGER_API_KEYApenas servidor.env (servidor)Alto (acesso à fila de tarefas)

Parte B - Verificações de implementação

Essas regras se aplicam a cada alteração de código.

B1 - Separar variáveis de ambiente

Visíveis pelo cliente:

NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY

Somente servidor:

SUPABASE_SERVICE_ROLE_KEY
DATABASE_URL
TRIGGER_API_KEY

Condição verificável

grep -r "NEXT_PUBLIC_" .env*

Secrets não podem aparecer ali.

Verificação de vazamento no build

grep -r "SERVICE_ROLE" .next/

Resultado esperado:

nenhum resultado

Cenário de falha

service_role no bundle do cliente significa:

  • Acesso completo ao banco de dados
  • RLS completamente ineficaz

Quem conhece os fundamentos da gestão de secrets entende por que essa separação é essencial.

B2 - Configurar o Supabase SSR Client corretamente

Server Client:

import { createServerClient } from "@supabase/ssr"

O servidor usa a anon key, não a service_role.

Admin Client:

createClient(url, SERVICE_ROLE_KEY)

Apenas para tarefas administrativas.

Condição verificável

grep -rn "SERVICE_ROLE" app/

Resultado esperado: apenas em

lib/supabase/admin.ts

Cenário de falha

Server Client com service_role:

  • O RLS é completamente ignorado
  • Todos os requests têm privilégios de admin

B3 - Middleware para Auth e Token Refresh

A middleware roda antes de cada request.

middleware.ts

Implementação

const { data: { user } } = await supabase.auth.getUser()

Não:

getSession()

Condição verificável

grep getUser middleware.ts

Cenário de falha

Sem middleware:

  • O Token Refresh não funciona
  • A sessão expira após 1 hora

B4 - Padrão de mutação

Todas as mutações seguem o mesmo fluxo.

Auth
Input Validation
Ownership Check
Mutation
Logging

Exemplo

const { user } = await supabase.auth.getUser()

if (!user) return

const post = await supabase
  .from("posts")
  .select("user_id")
  .eq("id", postId)

if (post.user_id !== user.id)
  throw new Error("forbidden")

Cenário de falha

Sem Ownership Check:

Estatística: De acordo com pesquisas do GitHub de 2024, mais de 23% dos vazamentos de secrets em projetos web envolvem chaves service_role ou tokens de backend equivalentes.

deletePost(id)

Um usuário pode excluir dados de outros.

B5 - Rate Limiting

O Next.js não possui Rate Limiting integrado.

Solução recomendada:

Upstash Redis

Exemplo

10 requests / minute / IP

Condição verificável

for i in {1..20}
do curl -X POST /api/login
done

Resultado esperado:

HTTP 429

B6 - Logging sem secrets

Os logs não podem conter:

JWT Tokens
service_role keys
emails
passwords

Condição verificável

grep console.log app/

Parte C - Operação

C1 - Atualização de dependências

Os releases do Next.js são frequentes.

Verificar semanalmente:

npm audit
npm outdated

Recomendado:

Renovate / Dependabot

C2 - Integração com Claude Code

Arquitetura:

Git Push
   |
Checks determinísticos
   |
Relatório de segurança
   |
Análise do Claude
   |
Decisão DevOps

Checks determinísticos

grep service_role
grep NEXT_PUBLIC
npm audit

Análise do Claude

O Claude verifica:

  • Novas Server Actions
  • Novas API Routes
  • Padrões de Ownership
  • Input Validation
  • Drift de arquitetura

O Claude não executa alterações em produção.

Checklist de deployment

Verificar antes de cada deployment:

[ ] Next.js roda como serviço próprio
[ ] Portas do Supabase fechadas externamente
[ ] Security Headers ativos

[ ] Nenhum service_role no bundle do cliente
[ ] service_role apenas no Admin Client

[ ] middleware.ts presente
[ ] Server Actions verificam Auth
[ ] Ownership Checks implementados

[ ] Rate Limit de login ativo

[ ] npm audit sem achados críticos

Conclusão

O Next.js em um stack moderno não é frontend, mas sim uma camada de servidor privilegiada.

A segurança surge através de três níveis:

Arquitetura
Verificações de implementação
Auditorias contínuas

A combinação de CI Security Checks e análise do Claude Code detecta tanto padrões conhecidos quanto novos riscos.

Quem segue esses princípios junto com uma arquitetura Cert-Ready by Design constrói segurança verificável em vez de auditorias posteriores.

Download da checklist de auditoria

Prompt preparado para o Claude Code. Faça upload do arquivo no seu servidor e inicie o Claude Code no diretório do projeto da sua aplicação Next.js. O Claude Code verificará automaticamente todos os pontos de segurança deste runbook e reportará APROVADO, AVISO ou CRÍTICO.

claude -p "$(cat claude-check-artikel-2-nextjs-br.md)" --allowedTools Read,Grep,Glob,Bash

Baixar checklist

Índice da série

  1. Supabase Self-Hosting Runbook
  2. Next.js sobre Supabase com segurança - este artigo
  3. Supabase Edge Functions com segurança
  4. Trigger.dev Background Jobs em produção segura
  5. Claude Code como controle de segurança no workflow DevOps
  6. Security Baseline para todo o stack

O próximo artigo descreve como Supabase Edge Functions são utilizadas com segurança - sem construir uma segunda arquitetura de backend.

Bert Gogolin

Bert Gogolin

Diretor Executivo, Gosign

AI Governance Briefing

IA empresarial, regulamentação e infraestrutura - uma vez por mês, diretamente de mim.

Sem spam. Cancelável a qualquer momento. Política de privacidade

Next.js Supabase Security DevOps Self-Hosting
Compartilhar este artigo

Perguntas frequentes

Por que o Next.js não pode rodar no mesmo container que o Supabase?

Quando o Next.js roda no mesmo container que o Supabase, o espaço de processos é compartilhado e os secrets ficam no mesmo environment. Um servidor Next.js comprometido teria acesso direto a todos os serviços de backend.

Por que a chave service_role não pode ser usada no código do cliente?

A chave service_role ignora todas as Row Level Security Policies. Se ela acabar no bundle do cliente, qualquer usuário terá acesso completo ao banco de dados e o RLS se torna completamente ineficaz.

O que acontece se uma Server Action não tem verificação de autenticação?

Sem uma verificação getUser() em uma Server Action, qualquer usuário com um cookie de sessão válido pode chamar a action, inclusive para dados de outros usuários. Como as Server Actions são acessíveis via HTTP POST, um simples curl com um cookie roubado é suficiente.