// 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:
- Cursor inlines hardcoded keys. Cursor'den "kimlik doğrulama hatasını düzeltmesini" istersiniz ve o, hizmet rolü istemcisi olduğunu varsayan bir Supabase örneğini yapıştırır. Anahtar, sayfa bileşeninin en üstünde sona erer. Hem anon istemcisi hem de hizmet istemcisi bir arada bulunur; ikisi de gemi.
- Claude Code defaults to permissive CORS. Generate Express / Fastify işleyicileri
cors({ origin: '*' })ile birlikte gönderilir çünkü bu, çalışma önizlemesi almanın en hızlı yoludur. Ara yazılım asla ikinci bir geçişe sahip olamaz. - Lovable and v0 skip the rules file. Firestore destekli projeler veri modelini oluşturur ancak nadiren
firestore.rules'ye dokunur. Test modu kuralları sessizce sona erer ve kullanıcıya hiçbir uyarı vermeden veritabanını kilitler. - Bolt skips RLS migrations. Bolt bir Supabase şeması ve anon anahtarını kullanan bir CRUD yüzeyi oluşturur.
ENABLE ROW LEVEL SECURITYhiçbir zaman geçişe girmez. Anonim kullanıcılar herhangi bir satırı okuyabilir veya yazabilir. - Windsurf trusts unsigned IDs. Oluşturulan
GET /api/items/[id], parametreyi okur ve mülkiyeti doğrulamadan Postgres'i sorgular. Desen, aktif active.idor-walking probunun onu tek bir taramada yüzeye çıkarmasını sağlayacak kadar yaygındır.
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ı
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 stringsHerhangi 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
# 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;` matchesHer 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
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
# 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.
// 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 varsayılan olarak etkin değildir ve FORCE kullanmadığınız sürece tablo sahipleri için zorunlu kılınmaz. Her tabloyu, rol başına açık politikalarla eşleştirin.
-- 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.
// 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.
// 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.
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 → ürün alan adınızı ekleyin → DNS TXT veya HTTP-dosya doğrulaması (tek adımlı). Doğrulandıktan sonra aktif taramalar kullanılabilir hale gelir ve planlanmış yeniden taramalar etkinleştirilebilir.
Pasif yeniden taramaları zamanlayın
Her gün Hobby'de, 3 saatte bir Pro'de, saatte bir Unlimited'de. Her çalıştırmada yeni bir bulgu ortaya çıkarsa size e-posta gönderilir ve abone olduysanız bir scan.completed web kancası tetiklenir.
# 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
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.
Canlı tehdit tespiti (Unlimited)
Sertifika şeffaflığı günlüğü farklılıkları, alanınız için verilen yeni TLS sertifikalarını ortaya çıkarır. DNS-kayıt farkları yetkisiz değişiklikleri yakalar. JS-paket gizli izleme, yeni bir anahtarın gönderilen pakete ulaştığı anda devreye girer. Tehdit istihbaratı beslemeleri (Spamhaus, URLhaus) listeleniyorsa alan adınızı bildirin.
Gerçek arıza modelleri ve bunların düzeltmeleri
Binlerce AI- oluşturulan uygulamadaki üretim taramasından elde edilen beş model, her biri gerçek düzeltmeyi içeriyor:
- İstemci bileşenindeki hizmet rolü anahtarı
Symptom:
baas.supabase-service-keyüretimde URL bulma. Cause: Cursor otomatik tamamlama,createClient(URL, SERVICE_ROLE_KEY)'yi bir React bileşenine yapıştırdı. Fix: hizmet istemcisini en üstteimport 'server-only'olacak şekildesrc/lib/supabase/service.tskonumuna taşıyın; istemci tarafında kullanım için anon anahtarını kullanarak paralel birsrc/lib/supabase/client.tsoluşturun; Supabase Studio aracılığıyla hizmet rolü anahtarını döndürün. - Firestore kuralları test modunda kaldı
Symptom:
baas.firebase-rulesyüksek önemde bulgu. Cause: oluşturulan kurallarallow read, write: if request.time < timestamp.date(2026, 6, 1);şeklinde okunur - zaman sınırlı bir "tümüne izin ver". Fix: her kuralı kimliği doğrulanmış kullanıcıya (match /users/{userId}/posts/{postId} { allow read, write: if request.auth.uid == userId; }) dahil edin vefirebase deploy --only firestore:rules'yi yeniden konuşlandırın. - Müsamahakar CORS üretime devam ediyor
Symptom:
active.corsyüksek önem. Cause: oluşturulan Express ara yazılımı:app.use(cors({ origin: '*' })). Fix: ön uç kaynağınızı izin verilenler listesine ekleyin:app.use(cors({ origin: ['https://your-app.com'], credentials: true })). Next.js API rotaları için yanıttaAccess-Control-Allow-Origin'yi açıkça ayarlayın. - RLS etkin ancak zorunlu değil
Symptom: etkin
baas.supabase-rls, kontrol panelinde RLS etkinleştirilmiş olsa bile anon rolünün genel bir tabloya yazabildiğini bildirir. Cause:ENABLEFORCEolmadan tablo sahibini muaf tutar ve taşıma işlemleri sahip olarak gerçekleştirilir. Fix: taşıma işleminealter table public.items force row level security;ekleyin. Yeniden konuşlandırın. - İmzasız IDOR-yürünebilir kimlikler
Symptom:
active.idor-walking, anon kullanıcısının kiracılar arasında/api/items/1,/api/items/2, ... okuyabildiğini bildirir. Cause: API işleyicisi yol parametresine güvenir ve Postgres'i sahiplik koşulu olmadan sorgular. Fix: her okuma sorgusuna.eq('user_id', user.id)ekleyin veya/api/users/[uid]/items/[id]kapsamına alınan imzalı URL'lere/UUID'lere gidin.
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.
- Generate fast — Cursor, Claude Code, Lovable, Bolt kullanın. Önemli olan bu.
- 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.
- Harden at deploy — ara yazılım, ortam ayırma, CSP tek seferlik, HSTS, yalnızca sunucu kimlik doğrulaması.
- 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.
- 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.
