FixVibe

// docs / security guides / hardening

Como proteger uma app construída com ferramentas de coding IA

Um guia passo a passo de proteção para aplicativos criados com Cursor, Claude Code, Lovable, Bolt, v0, Replit ou Windsurf. Quatro fases: entender por que os aplicativos gerados AI- falham de maneira diferente, executar uma auditoria imediata da base de código, fortalecer no momento da implantação e, em seguida, continuar monitorando. Opinativo, narrativo, com trechos reais que você pode copiar.

Por que AI- aplicativos gerados falham de maneira diferente

Os aplicativos codificados pelo Vibe podem ser seguros. Eles precisam de uma aprovação extra de auditoria porque os modos de falha são estruturais, e não descuidados:

  • Cursor inlines hardcoded keys. Você pede a Cursor para "corrigir o erro de autenticação" e ele cola um exemplo Supabase que assume um cliente com função de serviço. A chave termina no topo de um componente da página. Tanto o cliente anon quanto o cliente de serviço coexistem; ambos enviam.
  • Claude Code defaults to permissive CORS. Os manipuladores Generated Express / Fastify são fornecidos com cors({ origin: '*' }) porque essa é a maneira mais rápida de obter uma visualização funcional. O middleware nunca recebe uma segunda passagem.
  • Lovable and v0 skip the rules file. Projetos apoiados pelo Firestore geram o modelo de dados, mas raramente tocam firestore.rules. As regras do modo de teste expiram silenciosamente e bloqueiam o banco de dados sem aviso ao usuário.
  • Bolt skips RLS migrations. Bolt gera um esquema Supabase e uma superfície CRUD que usa a chave anon. ENABLE ROW LEVEL SECURITY nunca entra na migração. Usuários anônimos podem ler ou escrever qualquer linha.
  • Windsurf trusts unsigned IDs. Gerado GET /api/items/[id] lê o parâmetro e consulta o Postgres sem verificar a propriedade. O padrão é comum o suficiente para que a sonda ativa active.idor-walking o revele em uma única varredura.

A auditoria imediata: verifique sua base de código em busca de padrões de risco

Antes de endurecer qualquer coisa, encontre o que já está quebrado. Cada um desses greps leva menos de um minuto:

Segredos e chaves do provedor

bash
grep -RIn 'NEXT_PUBLIC_SUPABASE_SERVICE' src/
grep -RIn 'sk_live_\|pk_live_\|STRIPE_SECRET' src/
grep -RIn 'sk-ant-\|^sk-' src/  # Anthropic / OpenAI
grep -RIn 'AIza\|AKIA' src/        # Google / AWS
grep -RIn 'eyJh[A-Za-z0-9_-]\{20,\}' src/  # JWT-shaped strings

Qualquer hit precisa ser excluído e rotação de chave. Provider painéis: Supabase → Configurações → API, Stripe → Desenvolvedores → API teclas, console Anthropic / OpenAI.

Controles de acesso ao banco de dados

bash
# Supabase migrations
grep -RIn 'CREATE TABLE public\.' supabase/migrations/
grep -RIn 'ENABLE ROW LEVEL SECURITY\|FORCE ROW LEVEL SECURITY' supabase/migrations/

# Firebase / Firestore
cat firestore.rules  # confirm no `if true;` matches

Cada CREATE TABLE public.* precisa de um ENABLE ROW LEVEL SECURITY correspondente e de pelo menos uma política. As regras do Firestore devem definir o escopo das leituras para request.auth.uid.

Tratamento de autenticação e sessão

bash
grep -RIn 'getSession()' src/   # should be getUser() server-side
grep -RIn 'localStorage\.\(set\|get\)Item.*token' src/
grep -RIn 'jwt.verify.*\(noVerify\|skipVerify\)' src/

As rotas renderizadas pelo servidor devem usar supabase.auth.getUser() — verifica com o backend. getSession() lê um cookie não verificado. Os tokens em localStorage são acessíveis a qualquer script executado na página.

Cabeçalhos e middleware

bash
# Confirm middleware location for src/ layouts
ls src/middleware.ts middleware.ts 2>&1

# Look for CSP and security headers
grep -RIn 'Content-Security-Policy\|Strict-Transport-Security' src/

Com o layout src/, apenas src/middleware.ts é selecionado. Se o seu arquivo de middleware estiver na raiz do projeto, Next.js o ignorará silenciosamente e sua lógica CSP / auth-refresh nunca será executada.

Endurecimento no momento da implantação

Assim que a fonte estiver limpa, bloqueie como o aplicativo chega à produção.

Etapa 1: ambientes separados

Vercel: três ambientes — Production (seu domínio de produção), Visualização (PR / implantações de teste), Desenvolvimento (local). Cada um recebe seu próprio conjunto env-var. As teclas Live Stripe / Anthropic / Supabase nunca alcançam a Pré-visualização; As chaves de visualização nunca alcançam Production. As ramificações são enviadas para visualização automaticamente; mesclar para main implanta em Production.

Etapa 2: CSP estrito via middleware

Gere um nonce por solicitação e injete-o em Content-Security-Policy. Next.js aplica automaticamente o nonce às suas próprias tags de script quando você define o cabeçalho da solicitação x-nonce.

ts
// src/middleware.ts
import { NextResponse, type NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const nonce = crypto.randomUUID().replace(/-/g, '');
  const csp = [
    `script-src 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'self' 'unsafe-inline'`,
    `img-src 'self' data: https:`,
    `connect-src 'self' https://*.supabase.co`,
    `object-src 'none'`,
    `base-uri 'self'`,
    `frame-ancestors 'none'`,
  ].join('; ');

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);

  const response = NextResponse.next({ request: { headers: requestHeaders } });
  response.headers.set('Content-Security-Policy', csp);
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  return response;
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Etapa 3: Forçar RLS em todas as tabelas públicas

RLS não está habilitado por padrão e não é aplicado aos proprietários de tabelas, a menos que você FORCE faça isso. Combine cada tabela com políticas explícitas por função.

sql
-- supabase/migrations/XXXX_rls.sql
alter table public.profiles enable row level security;
alter table public.profiles force row level security;

create policy "profiles: read own"
  on public.profiles for select
  using (auth.uid() = id);

create policy "profiles: update own"
  on public.profiles for update
  using (auth.uid() = id)
  with check (auth.uid() = id);

Etapa 4: verificação de autenticação somente do servidor em cada rota API

Cada rota API de mudança de estado verifica o lado do servidor do chamador com supabase.auth.getUser(). O objeto do usuário se torna a fonte da verdade para user_id — nunca confie no corpo da solicitação para defini-lo.

ts
// src/app/api/items/route.ts
import { NextResponse, type NextRequest } from 'next/server';
import { createClient } from '@/lib/supabase/server';

export async function POST(request: NextRequest) {
  const supabase = await createClient();
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });

  const body = await request.json();
  const { data, error } = await supabase
    .from('items')
    .insert({ ...body, user_id: user.id })  // server-supplied, not from body
    .select()
    .single();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  return NextResponse.json(data);
}

Etapa 5: faça proxy reverso de suas análises

Proxying análises por meio de seu próprio domínio evita bloqueadores de anúncios e permite que seu CSP connect-src 'self' permaneça restrito. O mesmo padrão funciona para coletores de eventos personalizados PostHog, Plausíveis, Umami.

ts
// src/app/api/posthog/[...path]/route.ts
import { type NextRequest } from 'next/server';

const UPSTREAM = 'https://us.i.posthog.com';

export async function POST(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
  const { path } = await params;
  const url = `${UPSTREAM}/${path.join('/')}`;
  return fetch(url, {
    method: 'POST',
    headers: { 'content-type': req.headers.get('content-type') ?? 'application/json' },
    body: await req.text(),
  });
}

Etapa 6: proteção de redirecionamento aberto na rejeição pós-autenticação

Os fluxos de entrada/inscrição geralmente aceitam um parâmetro de consulta next. Rejeite qualquer coisa que não seja um caminho do mesmo site - comece com / e nunca // (relativo ao protocolo, envia os usuários para fora do site).

ts
function safeNext(raw: string | null): string {
  if (!raw) return '/dashboard';
  if (!raw.startsWith('/') || raw.startsWith('//')) return '/dashboard';
  return raw;
}

Em andamento: monitoramento e nova verificação

O desvio acontece em cada implantação. Trate a segurança como um loop, não como uma lista de verificação concluída.

Verifique seu domínio de produção

Dashboard → Domains → adicione seu domínio de produção → DNS TXT ou HTTP-verificação de arquivo (etapa única). Depois de verificadas, as verificações ativas ficam disponíveis e novas verificações agendadas podem ser ativadas.

Agende novas verificações passivas

Diariamente em Hobby, de 3 em 3 horas em Pro, de hora em hora em Unlimited. Cada execução envia um e-mail para você se uma nova descoberta aparecer e dispara um webhook scan.completed se você tiver se inscrito.

bash
# Or from CI, via the REST API:
curl -X POST https://fixvibe.app/api/v1/scans \
  -H "authorization: Bearer $FIXVIBE_TOKEN" \
  -H "content-type: application/json" \
  -d '{"target":"https://your-app.com"}'

Ativar verificações API-ativas (opcional)

Se você deseja sondagem ativa automatizada (SQLi / XSS / IDOR caminhada / etc.), ative-a por domínio em Painel → Domínios → API ativo. A autorização é durável, expira em 90 dias e é instantaneamente revogável. Combine com o webhook scan.active_api.first_used para que a primeira verificação ativa automatizada após a ativação chegue ao seu alerta.

Conecte as descobertas ao seu fluxo de trabalho AI

Mint an API token at Account → API tokens, then configure the MCP server (/docs/mcp) in Claude Desktop / Cursor / Continue. Ask your agent: "Run a scan on staging and show me the highest-severity findings." The agent calls FixVibe, fetches the report, and renders categorized remediation guidance so code/config fixes become prompts and DNS/provider/manual fixes become operator steps.

Detecção de ameaças ao vivo (Unlimited)

As diferenças de registro de transparência de certificado revelam novos certificados TLS emitidos para seu domínio. DNS-record diffs capturam alterações não autorizadas. O monitoramento secreto do JS-bundle é acionado no momento em que uma nova chave chega a um pacote enviado. Feeds de informações sobre ameaças (Spamhaus, URLhaus) relatam seu domínio se ele estiver listado.

Padrões reais de falhas e suas soluções

Cinco padrões de verificações de produção em milhares de aplicativos gerados AI-, cada um com a correção real:

  1. Chave de função de serviço em um componente cliente

    Symptom: baas.supabase-service-key descoberta sobre a produção URL. Cause: um Cursor preenchimento automático colado createClient(URL, SERVICE_ROLE_KEY) em um componente React. Fix: mova o cliente de serviço para src/lib/supabase/service.ts com import 'server-only' no topo; crie um src/lib/supabase/client.ts paralelo usando a chave anon para uso do lado do cliente; gire a chave da função de serviço por meio do Supabase Studio.

  2. Regras do Firestore deixadas no modo de teste

    Symptom: baas.firebase-rules descoberta de alta gravidade. Cause: regras geradas lidas allow read, write: if request.time < timestamp.date(2026, 6, 1); - um "permitir todos" com limite de tempo. Fix: define o escopo de cada regra para o usuário autenticado — match /users/{userId}/posts/{postId} { allow read, write: if request.auth.uid == userId; } — e reimplanta firebase deploy --only firestore:rules.

  3. Permissivo CORS sobrevivendo em produção

    Symptom: active.cors alta gravidade. Cause: middleware expresso gerado: app.use(cors({ origin: '*' })). Fix: coloque na lista de permissões sua origem de front-end: app.use(cors({ origin: ['https://your-app.com'], credentials: true })). Para rotas Next.js API, defina Access-Control-Allow-Origin explicitamente na resposta.

  4. RLS ativado, mas não forçado

    Symptom: ativo baas.supabase-rls relata que a função anon pode gravar em uma tabela pública mesmo que RLS esteja habilitado no painel. Cause: ENABLE sem FORCE deixa o proprietário da tabela isento - e as migrações são executadas como proprietário. Fix: anexe alter table public.items force row level security; à migração. Reimplantar.

  5. IDOR-IDs transitáveis não assinados

    Symptom: active.idor-walking relata que o usuário anon pode ler /api/items/1, /api/items/2, ... entre locatários. Cause: o manipulador API confia no parâmetro path e consulta o Postgres sem um predicado de propriedade. Fix: adicione .eq('user_id', user.id) em cada consulta de leitura ou mova para URLs/UUIDs assinados com escopo em /api/users/[uid]/items/[id].

O ciclo de segurança do código vibe

O objetivo não é a segurança perfeita; está eliminando os frutos mais fáceis de alcançar AI que as ferramentas perdem consistentemente para que você possa continuar enviando rapidamente.

  1. Generate fast — use Cursor, Claude Code, Lovable, Bolt. Esse é o ponto.
  2. Audit immediately — execute o conjunto grep acima, verifique RLS, verifique CSP, revise o limite de autenticação.
  3. Harden at deploy — middleware, separação de ambiente, CSP nonce, HSTS, verificação de autenticação somente de servidor.
  4. Monitor — FixVibe passivo diariamente, ativo semanalmente em um domínio verificado, webhooks para Slack, detecção de ameaças em Unlimited.
  5. Fix fast — use FixVibe coding-agent prompts for code/config findings and operator steps for DNS, provider, secret-rotation, or manual-review findings. Re-deploy, re-scan, close the loop.

Próximas etapas

Para obter o pano de fundo conceitual sobre DAST vs SAST e por que os aplicativos gerados por AI- precisam de sua própria verificação, leia AI-generated code security scanning. Para uma auditoria pré-envio de referência rápida, consulte vibe coding security checklist.

// escaneie seu app

Pare de ler. Comece a encontrar as falhas no seu.

Cole uma URL — o FixVibe roda todas as verificações passivas deste guia mais 200 outras em menos de um minuto. Grátis, sem instalação, sem cartão.

  • Free tier — 3 scans / mês, sem cartão.
  • Scans passivos contra qualquer URL — sem verificação de domínio.
  • Afinado para Cursor, Claude Code, Lovable, Bolt, v0, Replit.
  • Coding-agent prompts for code/config findings, plus operator steps for DNS/provider fixes.
Como proteger uma app construída com ferramentas de coding IA — Docs · FixVibe