FixVibe

// docs / security guides / hardening

So sicherst du eine App, die mit KI-Coding-Tools gebaut wurde

Eine Schritt-für-Schritt-Anleitung zum Härten von Apps, die Sie mit Cursor, Claude Code, Lovable, Bolt, v0, Replit oder Windsurf erstellt haben. Vier Phasen: Verstehen Sie, warum AI-generierte Apps unterschiedlich ausfallen, führen Sie ein sofortiges Codebasis-Audit durch, härten Sie es zum Zeitpunkt der Bereitstellung aus und führen Sie dann die Überwachung fort. Meinungsstark, erzählerisch, mit echten Ausschnitten zum Nachmachen.

Warum AI-generierte Apps unterschiedlich scheitern

Vibe-codierte Apps können sicher sein. Sie benötigen einen zusätzlichen Prüfdurchgang, da die Fehlerarten struktureller und nicht unachtsamer Natur sind:

  • Cursor inlines hardcoded keys. Sie bitten Cursor, „den Authentifizierungsfehler zu beheben“, und es wird ein Supabase-Beispiel eingefügt, das einen Client mit Dienstrolle annimmt. Der Schlüssel landet oben in einer Seitenkomponente. Sowohl der anonyme Client als auch der Service-Client existieren nebeneinander. beide Schiffe.
  • Claude Code defaults to permissive CORS. Generierte Express-/Fastify-Handler werden mit cors({ origin: '*' }) ausgeliefert, da dies der schnellste Weg ist, eine funktionierende Vorschau zu erhalten. Die Middleware erhält nie einen zweiten Durchgang.
  • Lovable and v0 skip the rules file. Von Firestore unterstützte Projekte generieren das Datenmodell, berühren aber selten firestore.rules. Testmodusregeln laufen stillschweigend ab und sperren die Datenbank, ohne dass der Benutzer eine Warnung erhält.
  • Bolt skips RLS migrations. Bolt generiert ein Supabase-Schema und eine CRUD-Oberfläche, die den anonymen Schlüssel verwendet. ENABLE ROW LEVEL SECURITY nimmt nie an der Migration teil. Anonyme Benutzer können jede Zeile lesen oder schreiben.
  • Windsurf trusts unsigned IDs. Das generierte GET /api/items/[id] liest den Parameter und fragt Postgres ab, ohne den Besitz zu überprüfen. Das Muster ist so häufig, dass die aktive active.idor-walking Sonde es innerhalb eines einzigen Scans entdeckt.

Die unmittelbare Prüfung: Durchsuchen Sie Ihre Codebasis nach Risikomustern

Bevor Sie etwas aushärten, finden Sie heraus, was bereits kaputt ist. Diese Greps dauern jeweils weniger als eine Minute:

Geheimnisse und Anbieterschlüssel

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

Jeder Treffer erfordert eine Löschung plus Schlüsselrotation. Provider-Dashboards: Supabase → Einstellungen → API, Stripe → Entwickler → API Schlüssel, Anthropic / OpenAI-Konsole.

Datenbankzugriffskontrollen

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

Jedes CREATE TABLE public.* benötigt ein passendes ENABLE ROW LEVEL SECURITY und mindestens eine Richtlinie. Firestore-Regeln müssen den Lesebereich auf request.auth.uid beschränken.

Authentifizierungs- und Sitzungsverwaltung

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/

Vom Server gerenderte Routen müssen supabase.auth.getUser() verwenden – die Überprüfung erfolgt mit dem Backend. getSession() liest ein nicht verifiziertes Cookie. Auf Token in localStorage kann jedes Skript zugreifen, das auf der Seite ausgeführt wird.

Header und 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/

Beim src/-Layout wird nur src/middleware.ts aufgenommen. Wenn sich Ihre Middleware-Datei im Projektstammverzeichnis befindet, wird sie von Next.js stillschweigend ignoriert und Ihre CSP / auth-refresh-Logik wird nie ausgeführt.

Verhärtung zum Zeitpunkt der Bereitstellung

Sobald die Quelle bereinigt ist, sperren Sie, wie die App in die Produktion gelangt.

Schritt 1: Separate Umgebungen

Vercel: drei Umgebungen – Production (Ihre Produktionsdomäne), Vorschau (PR / Staging-Bereitstellungen), Entwicklung (lokal). Jeder erhält seinen eigenen Env-Var-Satz. Live Stripe / Anthropic / Supabase Keys erreichen nie die Vorschau; Vorschauschlüssel erreichen nie Production. Zweige werden automatisch in die Vorschau verschoben; Zusammenführen zu main wird zu Production bereitgestellt.

Schritt 2: Striktes CSP über Middleware

Generieren Sie eine Nonce pro Anfrage und fügen Sie sie dann in Content-Security-Policy ein. Next.js wendet die Nonce automatisch auf seine eigenen Skript-Tags an, wenn Sie den x-nonce-Anforderungsheader festlegen.

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).*)'],
};

Schritt 3: Erzwingen Sie RLS für jede öffentliche Tabelle

RLS ist standardmäßig nicht aktiviert und wird für Tabellenbesitzer nicht erzwungen, es sei denn, Sie FORCE es. Koppeln Sie jede Tabelle mit expliziten Richtlinien pro Rolle.

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);

Schritt 4: Nur-Server-Authentifizierungsüberprüfung auf jeder API-Route

Jede zustandsverändernde API-Route verifiziert den Aufrufer serverseitig mit supabase.auth.getUser(). Das Benutzerobjekt wird zur Quelle der Wahrheit für user_id – vertrauen Sie niemals einem Anforderungstext, der es festlegt.

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);
}

Schritt 5: Reverse-Proxy Ihrer Analysen

Proxying-Analysen über Ihre eigene Domain vermeiden Werbeblocker und sorgen dafür, dass Ihr CSP connect-src 'self' eng bleibt. Das gleiche Muster funktioniert für PostHog, Plausible, Umami und benutzerdefinierte Ereignissenken.

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(),
  });
}

Schritt 6: Öffnen Sie den Redirect-Schutz für den Post-Authentifizierungs-Bounce

Anmelde-/Registrierungsabläufe akzeptieren üblicherweise einen next-Abfrageparameter. Lehnen Sie alles ab, was kein Pfad zur gleichen Site ist – beginnen Sie mit / und niemals mit // (protokollbezogen, leitet Benutzer von der Site ab).

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

Laufend: Überwachung und erneutes Scannen

Bei jedem Einsatz kommt es zu Drift. Behandeln Sie Sicherheit als eine Schleife und nicht als eine Checkliste, die Sie durcharbeiten.

Überprüfen Sie Ihre Produktionsdomäne

Dashboard → Domains → Fügen Sie Ihre Produktdomäne hinzu → DNS TXT oder HTTP-Dateiüberprüfung (Einzelschritt). Nach der Überprüfung stehen aktive Scans zur Verfügung und geplante erneute Scans können aktiviert werden.

Planen Sie passive erneute Scans

Täglich um Hobby, 3-stündlich um Pro, stündlich um Unlimited. Bei jedem Durchlauf erhalten Sie eine E-Mail, wenn ein neuer Befund erscheint, und löst einen scan.completed-Webhook aus, wenn Sie sich angemeldet haben.

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"}'

API-aktive Scans aktivieren (optional)

Wenn Sie eine automatisierte aktive Prüfung (SQLi / XSS / IDOR Gehen / usw.) wünschen, schalten Sie sie pro Domäne unter Dashboard → Domänen → API aktiv ein. Die Autorisierung ist dauerhaft, läuft 90 Tage ab und kann sofort widerrufen werden. Koppeln Sie es mit dem scan.active_api.first_used-Webhook, damit der erste automatisierte aktive Scan nach der Aktivierung Ihre Benachrichtigung erreicht.

Integrieren Sie die Ergebnisse in Ihren AI Workflow

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.

Live-Bedrohungserkennung (Unlimited)

Zertifikatstransparenz-Protokollunterschiede zeigen neue TLS Zertifikate an, die für Ihre Domain ausgestellt wurden. DNS-Datensatzunterschiede fangen nicht autorisierte Änderungen ab. Die Überwachung des JS-Bundle-Geheimnisses wird ausgelöst, sobald ein neuer Schlüssel ein versendetes Bundle erreicht. Threat-Intel-Feeds (Spamhaus, URLhaus) melden Ihre Domain, wenn sie aufgeführt ist.

Echte Fehlermuster und deren Behebung

Fünf Muster aus Produktionsscans in Tausenden von AI-generierten Apps, jeweils mit der tatsächlichen Fehlerbehebung:

  1. Dienstrollenschlüssel in einer Clientkomponente

    Symptom: baas.supabase-service-key Feststellung zur Produktion URL. Cause: eine Cursor automatische Vervollständigung, die createClient(URL, SERVICE_ROLE_KEY) in eine React-Komponente eingefügt hat. Fix: verschiebt den Service-Client nach src/lib/supabase/service.ts mit import 'server-only' oben; Erstellen Sie ein paralleles src/lib/supabase/client.ts mit dem anonymen Schlüssel für die clientseitige Verwendung. Drehen Sie den Dienstrollenschlüssel über Supabase Studio.

  2. Firestore-Regeln bleiben im Testmodus

    Symptom: baas.firebase-rules Befund mit hohem Schweregrad. Cause: generierte Regeln lauten allow read, write: if request.time < timestamp.date(2026, 6, 1); – ein zeitbegrenztes „Alles zulassen“. Fix: beschränkt jede Regel auf den authentifizierten Benutzer – match /users/{userId}/posts/{postId} { allow read, write: if request.auth.uid == userId; } – und stellt firebase deploy --only firestore:rules erneut bereit.

  3. Freizügiges CORS Überleben in der Produktion

    Symptom: active.cors hoher Schweregrad. Cause: generierte Express-Middleware: app.use(cors({ origin: '*' })). Fix: Setzen Sie Ihren Frontend-Ursprung auf die Zulassungsliste: app.use(cors({ origin: ['https://your-app.com'], credentials: true })). Für Next.js API Routen legen Sie Access-Control-Allow-Origin explizit in der Antwort fest.

  4. RLS aktiviert, aber nicht erzwungen

    Symptom: aktiv baas.supabase-rls meldet, dass die anonyme Rolle in eine öffentliche Tabelle schreiben kann, obwohl RLS im Dashboard aktiviert ist. Cause: ENABLE ohne FORCE lässt den Tabelleneigentümer ausgenommen – und Migrationen werden als Eigentümer ausgeführt. Fix: alter table public.items force row level security; an die Migration anhängen. Erneut bereitstellen.

  5. Nicht signierte IDOR-begehbare IDs

    Symptom: active.idor-walking meldet, dass der anonyme Benutzer /api/items/1, /api/items/2, ... mandantenübergreifend lesen kann. Cause: Der API Handler vertraut dem Pfadparameter und fragt Postgres ohne Besitzprädikat ab. Fix: Fügen Sie .eq('user_id', user.id) bei jeder Leseabfrage hinzu oder wechseln Sie zu signierten URLs/UUIDs mit dem Gültigkeitsbereich /api/users/[uid]/items/[id].

Die Vibe-Code-Sicherheitsschleife

Das Ziel ist nicht perfekte Sicherheit; Es eliminiert die niedrig hängenden Früchte, die AI Tools ständig übersehen, sodass Sie weiterhin schnell versenden können.

  1. Generate fast – verwenden Sie Cursor, Claude Code, Lovable, Bolt. Das ist der Punkt.
  2. Audit immediately – Führen Sie den oben angegebenen grep-Satz aus, überprüfen Sie RLS, überprüfen Sie CSP und überprüfen Sie die Authentifizierungsgrenze.
  3. Harden at deploy – Middleware, Umgebungstrennung, CSP Nonce, HSTS, reine Server-Authentifizierungsüberprüfung.
  4. Monitor – FixVibe täglich passiv, wöchentlich aktiv auf einer verifizierten Domain, Webhooks zu Slack, Bedrohungserkennung auf 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.

Nächste Schritte

Für den konzeptionellen Hintergrund zu DAST vs. SAST und warum AI-generierte Apps einen eigenen Scan benötigen, lesen Sie AI-generated code security scanning. Eine Kurzreferenz vor dem Versand finden Sie im vibe coding security checklist.

// deine App scannen

Genug gelesen. Finde die Lücken in deiner App.

Eine URL einfügen — FixVibe führt jeden passiven Check aus diesem Guide plus 200 weitere in unter einer Minute aus. Kostenlos, ohne Installation, ohne Karte.

  • Free-Tier — 3 Scans / Monat, ohne Karte.
  • Passive Scans gegen jede URL — keine Domain-Verifizierung nötig.
  • Abgestimmt auf Cursor, Claude Code, Lovable, Bolt, v0, Replit.
  • Coding-agent prompts for code/config findings, plus operator steps for DNS/provider fixes.
Kostenlosen Scan starten

keine Anmeldung nötig