FixVibe

// docs / security guides / hardening

AI kodlama araçlarıyla yapılmış bir uygulamayı nasıl güvenli hale getirirsiniz

Cursor, Claude Code, Lovable, Bolt, v0, Replit veya Windsurf ile oluşturduğunuz uygulamalar için adım adım sağlamlaştırma kılavuzu. Dört aşama: AI-oluşturulan uygulamaların neden farklı şekilde başarısız olduğunu anlayın, anında bir kod tabanı denetimi çalıştırın, dağıtım zamanında sağlamlaştırın ve ardından izlemeye devam edin. Fikir odaklı, anlatımlı, gerçek parçalarla kopyalayabilirsiniz.

AI-oluşturulan uygulamalar neden farklı şekilde başarısız oluyor?

Vibe kodlu uygulamalar güvenli olabilir. Arıza modları dikkatsiz değil yapısal olduğu için fazladan bir denetim geçişine ihtiyaçları var:

  • Assistants inline hardcoded keys. You ask for a fix to an auth error and get a pasted Supabase example that assumes a service-role client. The key ends up at the top of a page component. Both the anon client and the service client coexist; both ship.
  • Generated servers default to permissive CORS. Generated Express / Fastify handlers often ship with cors({ origin: '*' }) because that's the fastest way to get a working preview. The middleware never gets a second pass.
  • Rules files get skipped. Firestore-backed projects generate the data model but rarely touch firestore.rules. Test-mode rules let anyone read and overwrite data until someone replaces them.
  • RLS never enters the migration. A generated Supabase schema and CRUD surface use the anon key, but ENABLE ROW LEVEL SECURITY never enters the migration. Anonymous users can read or write any row.
  • Handlers trust IDs. A generated GET /api/items/[id] reads the param and queries Postgres without verifying ownership. Active scans on a verified domain test for this (IDOR / BOLA).

Acil denetim: risk kalıpları için kod tabanınızı gravürleyin

Herhangi bir şeyi sertleştirmeden önce zaten kırılmış olanı bulun. Bu greplerin her biri bir dakikadan az sürüyor:

Sırlar ve sağlayıcı anahtarları

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

Herhangi bir vuruşun silinmesi ve anahtar döndürmesi gerekir. Provider kontrol panelleri: Supabase → Ayarlar → API, Stripe → Geliştiriciler → API tuşları, Antropik / OpenAI konsolu.

Veritabanı erişim kontrolleri

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

Her CREATE TABLE public.*'nin eşleşen bir ENABLE ROW LEVEL SECURITY ve en az bir politikaya ihtiyacı vardır. Firestore kuralları, okumaları request.auth.uid kapsamına almalıdır.

Kimlik doğrulama ve oturum yönetimi

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/

Sunucu tarafından oluşturulan rotalar supabase.auth.getUser() kullanmalıdır; arka uçla doğrulanır. getSession() doğrulanmamış bir çerezi okur. localStorage içindeki belirteçlere sayfada çalışan herhangi bir komut dosyası tarafından erişilebilir.

Başlıklar ve ara yazılım

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/

src/ düzeniyle yalnızca src/middleware.ts seçilir. Ara yazılım dosyanız proje kökündeyse, Next.js onu sessizce yok sayar ve CSP / auth-refresh mantığınız hiçbir zaman çalışmaz.

Dağıtım zamanında sertleşme

Kaynak temizlendikten sonra uygulamanın üretime ulaşma şeklini kilitleyin.

1. Adım: Ortamları ayırın

Vercel: üç ortam — Production (üretim alanınız), Önizleme (PR / hazırlama dağıtımları), Geliştirme (yerel). Her biri kendi env-var setini alır. Canlı Stripe / Antropik / Supabase tuşları hiçbir zaman Önizlemeye ulaşmaz; Önizleme tuşları hiçbir zaman Production'a ulaşmaz. Dallar otomatik olarak Önizleme'ye geçer; main ile birleştirme, Production'a dağıtılır.

Adım 2: Ara katman yazılımı aracılığıyla CSP katı kuralları

İstek başına bir kez oluşturun ve bunu Content-Security-Policy içine enjekte edin. Next.js, x-nonce istek başlığını ayarladığınızda nonce'ı kendi komut dosyası etiketlerine otomatik olarak uygular.

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

3. Adım: Her genel masada RLS'ı zorunlu kılın

RLS isn't enabled by default on tables you create in SQL or migrations. Enable it on every exposed table and pair each one with explicit policies per role — that is what stops the anon and authenticated roles. FORCE only makes the table owner obey RLS too.

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

Adım 4: Her API rotasında yalnızca sunucu kimlik doğrulaması

Durum değiştiren her API rotası, arayan sunucu tarafını supabase.auth.getUser() ile doğrular. Kullanıcı nesnesi user_id için gerçeğin kaynağı haline gelir; onu ayarlamak için asla bir istek gövdesine güvenmeyin.

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

5. Adım: Analitiklerinizi ters proxy ile temsil edin

Proxy analitiğini kendi alanınız üzerinden yapmak, reklam engelleyicileri önler ve CSP connect-src 'self' alanınızın dar kalmasını sağlar. Aynı model PostHog, Plausible, Umami, özel olay havuzları için de geçerlidir.

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

Adım 6: Kimlik doğrulama sonrası geri dönüşte açık yönlendirme koruması

Oturum açma/kaydolma akışları genellikle next sorgu parametresini kabul eder. Aynı site yolu olmayan her şeyi reddedin; / ile başlayın ve asla // (protokole bağlı, kullanıcıları site dışına gönderir) ile başlayın.

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

Devam ediyor: izleme ve yeniden tarama

Drift her konuşlandırmada gerçekleşir. Güvenliği, tamamladığınız bir kontrol listesi olarak değil, bir döngü olarak ele alın.

Üretim alanınızı doğrulayın

Dashboard → Domains → add your production domain → DNS TXT or HTTP-file verification. Active scans require Hobby or above; scheduled re-scans require Pro or Unlimited.

Pasif yeniden taramaları zamanlayın

Scheduled re-scans are available on Pro and Unlimited for verified domains. Free and Hobby scans are manual. Scheduled scans use your plan allowance. Configure completion email preferences and a scan.completed webhook if needed.

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-aktif taramaları etkinleştirin (isteğe bağlı)

Otomatik etkin tarama (SQLi / XSS / IDOR yürüme / vb.) istiyorsanız, Kontrol Paneli → Etki Alanları → API etkin bölümünden bunu etki alanı başına açın. Yetkilendirme kalıcıdır, süresi 90 gündür ve anında iptal edilebilir. Etkinleştirmeden sonraki ilk otomatik aktif taramanın uyarınıza ulaşması için scan.active_api.first_used web kancasıyla eşleştirin.

Bulguları AI iş akışınıza bağlayın

On Hobby or above, create an API token at Account → API tokens and configure the MCP server (/docs/mcp) in your coding tool. Ask your agent to run an authorized scan and inspect the highest-severity findings. Code fixes can use remediation prompts; provider and DNS fixes may need manual operator steps.

Canlı tehdit tespiti (Unlimited)

Periodic certificate-transparency, DNS, JS-bundle, and threat-intelligence checks report observed changes on supported signals. Alerts depend on successful polling and source availability; they do not establish continuous or complete security coverage.

Gerçek arıza modelleri ve bunların düzeltmeleri

Five common patterns in AI-generated apps, each with the actual fix:

  1. İstemci bileşenindeki hizmet rolü anahtarı

    Symptom: FixVibe reports an exposed Supabase service-role key on the production URL. Cause: an autocomplete pasted createClient(URL, SERVICE_ROLE_KEY) into a React component. Fix: move the service client to src/lib/supabase/service.ts with import 'server-only' at the top; create a parallel src/lib/supabase/client.ts using the anon key for client-side use; rotate the service-role key via Supabase Studio.

  2. Firestore kuralları test modunda kaldı

    Symptom: a high-severity open Firebase rules finding. Cause: generated rules read allow read, write: if request.time < timestamp.date(2026, 6, 1); — a time-bounded "allow all". Fix: scope each rule to the authenticated user — match /users/{userId}/posts/{postId} { allow read, write: if request.auth.uid == userId; } — and re-deploy firebase deploy --only firestore:rules.

  3. Müsamahakar CORS üretime devam ediyor

    Symptom: a high-severity CORS misconfiguration finding. Cause: generated Express middleware: app.use(cors({ origin: '*' })). Fix: allowlist your frontend origin: app.use(cors({ origin: ['https://your-app.com'], credentials: true })). For Next.js API routes, set Access-Control-Allow-Origin explicitly in the response.

  4. RLS etkin ancak zorunlu değil

    Symptom: FixVibe reports that anonymous visitors can read a public table even though RLS looks enabled in the dashboard. Cause: RLS is on, but a policy such as USING (true) lets the anon role through, or the migration that tightened it never ran in production. Fix: replace the permissive policy with one scoped to auth.uid(), apply the migration, and re-scan.

  5. İmzasız IDOR-yürünebilir kimlikler

    Symptom: an active scan on your verified domain reports that one user can read another user's records at /api/items/1, /api/items/2, ... Cause: the API handler trusts the path param and queries Postgres without an ownership predicate. Fix: add .eq('user_id', user.id) on every read query, or move to signed URLs / UUIDs scoped under /api/users/[uid]/items/[id].

Vibe kodu güvenlik döngüsü

Amaç mükemmel güvenlik değil; AI araçlarının sürekli olarak gözden kaçırdığı düşük asılı meyveleri ortadan kaldırır, böylece nakliyeyi hızlı bir şekilde sürdürebilirsiniz.

  1. Generate fast — Cursor, Claude Code, Lovable, Bolt kullanın. Önemli olan bu.
  2. Audit immediately — yukarıdaki grep ayarını çalıştırın, RLS'yi işaretleyin, CSP'yi doğrulayın, kimlik doğrulama sınırını inceleyin.
  3. Harden at deploy — ara yazılım, ortam ayırma, CSP tek seferlik, HSTS, yalnızca sunucu kimlik doğrulaması.
  4. Monitor — FixVibe günlük pasif, doğrulanmış bir alanda haftalık olarak aktif, Slack'e yönelik web kancaları, Unlimited üzerinde tehdit algılama.
  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.

Sonraki adımlar

DAST ile SAST arasındaki kavramsal arka plan ve AI-oluşturulan uygulamaların neden kendi taramalarına ihtiyaç duyduğu hakkında bilgi edinmek için AI-generated code security scanning bölümünü okuyun. Hızlı referans gönderim öncesi denetimi için vibe coding security checklist adresine bakın.

// scan your app

Okumayı bırak. Kendi uygulamandaki açıkları bulmaya başla.

Drop in a URL — FixVibe runs every passive check from this guide plus the rest of its 230+ passive checks, usually in under a minute. Free, no install, no card.

  • Free katman — ayda 3 tarama, kart yok.
  • Herhangi bir URL'ye karşı pasif taramalar — etki alanı doğrulaması gerekmez.
  • Cursor, Claude Code, Lovable, Bolt, v0, Replit için ayarlandı.
  • Coding-agent prompts for code/config findings, plus operator steps for DNS/provider fixes.
AI kodlama araçlarıyla yapılmış bir uygulamayı nasıl güvenli hale getirirsiniz · FixVibe