DEVSECOPS

Vibe coding güvenliği: "Açıkları kapat" demek neden yetmiyor?

10 projede iki yolu karşılaştırdım: sonradan "tara ve açıkları kapat" demek mi, en baştan security md ile geliştirmek mi? Tarayıp kapatmak yetmiyor.

Tek cümlelik özet

Tara-kapat bulguların %55'ini kapattı ama authorization ve iş mantığı hatalarının %73'ü yerinde kaldı; security md ile baştan geliştirilen projelerde bulgu sayısı %81 daha azdı.

Vibe coding ile proje çıkaran herkesin bir noktada yazdığı bir prompt var: "Projeyi baştan sona tara ve güvenlik açıklarını kapat." Ben de yazdım. Model birkaç dosyayı değiştiriyor, "12 açık kapatıldı" gibi bir özet veriyor ve insan rahatlıyor.

Bu rahatlığın ne kadar hak edilmiş olduğunu merak ettim, çünkü pentest tarafında gördüğüm tablo bununla pek uyuşmuyordu. O yüzden küçük bir araştırma yaptım. 10 farklı projede iki yolu karşılaştırdım: işi bitirip sonradan taratmak ve en baştan security md dosyalarıyla geliştirmek.

Sonucu baştan söyleyeyim: "zafiyeti kapat" demek yetmiyor. Yazının geri kalanı bunun nedenini ve yerine ne yapılabileceğini anlatıyor.

Neyi, nasıl test ettim?

10 proje seçtim. Hepsi farklı dil ve altyapıda, hepsi vibe coding ile üretilebilecek boyutta: giriş ekranı, birkaç rol, veritabanı, dosya yükleme ya da ödeme gibi bir iki hassas akış.

Her projeyi üç durumda inceledim:

  1. Ham hali. Sadece özellik isteyen prompt'larla geliştirildi, güvenlikle ilgili tek kelime yok.
  2. Tara-kapat. Ham halin üzerine tek bir prompt: "Projeyi baştan sona tara ve bulduğun güvenlik açıklarını kapat."
  3. Security md. Aynı proje, aynı özellik prompt'larıyla sıfırdan. Tek fark, ilk prompt'tan önce repoya bir SECURITY.md ve onu referans veren bir CLAUDE.md koymam.

Üç durumu da aynı şekilde test ettim: OWASP ASVS'ten çıkardığım sabit bir kontrol listesi, statik analiz ve elle yaptığım klasik web/API testleri. Bulgu sayarken aynı hatanın farklı endpoint'lerdeki tekrarını ayrı saydım, çünkü saldırgan için her biri ayrı bir kapı.

Rakamlar ne diyor?

# Proje Stack Ham Tara-kapat Security md
1 SaaS yönetim paneli Next.js + Prisma 24 10 4
2 Randevu API'si Django + DRF 19 8 3
3 E-ticaret Laravel 27 13 6
4 Chat backend Express + MongoDB 23 11 5
5 Dosya paylaşım servisi FastAPI + PostgreSQL 21 9 4
6 Fatura servisi Spring Boot 17 8 3
7 URL kısaltıcı Go + Gin 14 6 2
8 CRM ASP.NET Core 20 9 4
9 Mobil uygulama React Native + Supabase 26 13 6
10 Forum Rails 23 10 4
Toplam 214 97 41

Ham halde 10 projede toplam 214 bulgu çıktı ve bunların 61'i yüksek ya da kritik seviyedeydi. Tara-kapat sonrasında 97 bulgu kaldı, yani yaklaşık %55'i kapandı. Security md ile baştan geliştirilen sürümlerde ise toplam 41 bulgu vardı; ham hale göre %81 daha az.

Yüksek ve kritik bulgularda fark daha da açık: ham halde 61, tara-kapat sonrası 27, security md ile 9.

İlk bakışta %55 fena görünmüyor. Asıl hikâye, kapananların ve kalanların hangi türden olduğuna bakınca ortaya çıkıyor.

Tara-kapat neyi kapatıyor, neyi kaçırıyor?

Bulguları iki gruba ayırdım.

Kalıp hataları: SQL injection, XSS, hardcoded secret, zayıf hash, eksik security header, eski dependency. Bunların ortak özelliği, koda bakınca görünmeleri. Yanlış satır orada duruyor.

Tasarım hataları: eksik authorization, IDOR, tenant izolasyonu, rate limit, session yönetimi, iş mantığı. Bunlarda yanlış bir satır yok. Olması gereken bir kontrol hiç yazılmamış.

Grup Ham Tara-kapat sonrası Kapanma oranı
Kalıp hataları 118 14 %88
Tasarım hataları 96 70 %27
Fix sırasında oluşan yeni bulgular - 13 -

Model kalıp hatalarında gerçekten iyi; 118 bulgunun 104'ünü kapattı. Tasarım hatalarında ise 96 bulgunun 70'i yerinde kaldı. Yani en çok can yakan grupta, her dört bulgudan yaklaşık üçü tara-kapat'tan sağ çıktı.

Bir de 13 yeni bulgu var. Bunlar fix sırasında oluştu. Örneğin bir projede model input validation eklerken hata mesajına stack trace koydu. Bir diğerinde authorization kontrolünü bir endpoint'e ekledi ama aynı veriyi dönen export endpoint'ini unuttu, üstüne de özet kısmına "authorization eklendi" yazdı.

Merak edip aynı prompt'u ikinci kez verdim. 10 projede toplam 9 bulgu daha kapandı. Üçüncü turda neredeyse hiçbir şey değişmedi. Demek ki sorun prompt'un kendisinde; aynı şeyi tekrar tekrar istemek sonucu değiştirmiyor.

Neden yetmiyor?

Bunu kendime dört maddeyle açıkladım.

Scanner yazılmış kodu görür, yazılmamış kodu göremez. findById(req.params.id) satırı tek başına hatalı görünmüyor. Hatalı olan, o kaydın kime ait olduğunu soran satırın hiç yazılmamış olması. Bunu yakalamak için "bu veri kimin?" sorusunun cevabını bilmek gerekiyor ve bu cevap kodda değil, sizin kafanızda.

Fix lokal kalıyor. Model bulduğu yeri yamıyor. Aynı hata başka üç endpoint'te duruyorsa ve o dosyalar o an context'te değilse, yerlerinde kalıyorlar. Laravel projesinde sipariş detayındaki IDOR kapandı, fatura PDF'indeki aynı IDOR kaldı.

Tasarım kararları çoktan verilmiş oluyor. Tenant ayrımı en baştan düşünülmediyse, bunu sonradan eklemek bütün query'leri değiştirmek demek. "Açıkları kapat" prompt'u bu büyüklükte bir işe girmiyor. Model en küçük değişiklikle yetiniyor, bu da çoğu zaman bir yama oluyor.

Sonraki özellik eski alışkanlıkla yazılıyor. Bana göre en önemlisi bu. Taramayı yaptınız, açıkları kapattınız, ertesi gün yeni bir endpoint istediniz. Model o endpoint'i yazarken dünkü taramadan haberi yok. Kural hiçbir yerde yazmadığı için aynı hata geri geliyor.

Security md ne değiştiriyor?

Security md, modelin her kod üretişinde context'inde duran kısa bir kural dosyası. Tara-kapat bir kerelik bir olay, md ise sürekli orada. Fark buradan geliyor.

Kullandığım dosyanın sadeleştirilmiş hali şuna benziyor:

MARKDOWN
# SECURITY.md

## Veri sahipliği
- Her tablo bir `owner_id` ya da `tenant_id` taşır.
- Her okuma ve yazma query'si bu alanla filtrelenir. İstisna yok.

## Authentication ve authorization
- Varsayılan: her endpoint login ister. Public olanlar açıkça listelenir.
- Rol kontrolü handler'ın içinde değil, tek bir middleware'de yapılır.
- Client'tan gelen `user_id`, `role`, `price` alanlarına güvenilmez.

## Input ve output
- Bütün query'ler parametreli. String birleştirme ile SQL yazılmaz.
- Hata mesajında stack trace, query ya da dosya yolu dönmez.

## Secret ve dependency
- Secret'lar sadece environment variable'dan okunur.
- Yeni paket eklemeden önce paketin gerçekten var olduğunu kontrol et.

## Her yeni endpoint için
- Yetkisiz kullanıcı ve başka kullanıcının kaydı için test yaz.
- Rate limit gerekiyor mu? Gerekmiyorsa nedenini yorum olarak yaz.

Dosyanın uzun olmasına gerek yok. Benim denemelerimde 60-80 satırı geçen dosyalar daha iyi sonuç vermedi; model uzun listelerde bazı maddeleri atlamaya başladı. OpenSSF'in rehberi de aynı şeyi söylüyor: talimatlar kısa ve somut olsun. Aynı rehber "bir güvenlik uzmanı gibi davran" tarzı persona prompt'larının işe yaramadığını, hatta bazı çalışmalarda daha kötü sonuç verdiğini de not ediyor.

Md'nin en çok fark yarattığı yer beklediğim gibi tasarım hataları oldu: 96 bulgudan 29'a indi. Çünkü "her query owner_id ile filtrelenir" kuralı, o eksik satırı modelin her seferinde yazmasını sağlıyor. Tara-kapat'ın bulamadığı boşluk hiç oluşmuyor.

Claude bu güvenliği nasıl sağlıyor?

Claude Code tarafında bunu tek bir özellik yapmıyor. Üst üste binen birkaç katman var ve hangisinin ne işe yaradığını bilmek gerekiyor.

CLAUDE.md. Projenin kök dizinindeki bu dosya her session'da otomatik yükleniyor. Security kurallarınızı buraya yazabilir ya da buradan SECURITY.md'ye referans verebilirsiniz. Araştırmadaki üçüncü durum tam olarak buydu.

security-guidance plugin. Anthropic'in resmi plugin'i ve bence konunun en ilginç parçası. Claude'un kendi yazdığı kodu üç noktada kontrol ediyor: her dosya edit'inde hızlı bir pattern kontrolü (eval(, innerHTML, pickle gibi), her turn'ün sonunda değişen diff'in ayrı bir Claude tarafından review edilmesi ve her commit'te çevredeki kodu da okuyan daha derin bir review. Review'u yapan, kodu yazan instance değil; temiz bir context ile başlayan ayrı bir çağrı. Kendi kurallarınızı .claude/claude-security-guidance.md dosyasına ekleyebiliyorsunuz.

Text
/plugin install security-guidance@claude-plugins-official

/security-review. İstediğiniz anda branch'teki değişiklikler üzerinde bir güvenlik turu çalıştırıyor. Aynı işi PR'larda otomatik yapan bir GitHub Action da var.

Permission ve sandbox. Bunlar yazılan kodu değil, agent'ın kendisini sınırlıyor. Hangi komutu sormadan çalıştırabileceği, hangi dizine yazabileceği, network'e çıkıp çıkamayacağı buradan belirleniyor.

Burada dürüst olmak gerekiyor: Anthropic'in dokümanı da md kurallarının "guardrail değil, rehber" olduğunu açıkça yazıyor. Plugin hiçbir yazmayı ya da commit'i bloklamıyor, bulguyu Claude'a geri veriyor ve review modeli bir şeyi kaçırabilir. Kesin engelleme istiyorsanız bunu hook ya da CI kontrolü ile yapmanız gerekiyor.

Bir de madalyonun öbür yüzü var. Kural dosyaları otomatik yüklendiği ve güvenilir kabul edildiği için kendileri de bir saldırı yüzeyi. Cloud Security Alliance'ın Ekim 2026 notu ve OWASP'ın cheat sheet'i aynı uyarıyı yapıyor: CLAUDE.md, AGENTS.md gibi dosyalardaki değişiklikleri CI config'i kadar ciddiye alın ve review'suz merge etmeyin.

İkisini birlikte kullanınca

Son olarak security md ile geliştirilen 10 projenin üzerine bir de review katmanı koydum: security-guidance plugin açık, her özellikten sonra /security-review. Toplam bulgu 41'den 17'ye, yüksek seviyeli bulgu 9'dan 2'ye indi.

Durum Toplam bulgu Yüksek / kritik
Ham 214 61
Tara-kapat 97 27
Security md 41 9
Security md + review 17 2

Buradan çıkardığım sonuç şu: tarama kötü bir fikir değil, sadece tek başına ve en sonda yapıldığında zayıf kalıyor. Md hatanın oluşmasını zorlaştırıyor, review ise yine de kaçanı yakalıyor. Sıra önemli.

Kalan 17 bulgunun neredeyse tamamı iş mantığıyla ilgiliydi. Kupon iki kez kullanılabiliyor, iptal edilen sipariş yine de kargoya gidiyor gibi şeyler. Bunları ne md ne de review yakaladı, çünkü neyin doğru olduğunu sadece işi bilen kişi biliyor. O kısım hâlâ bizde.

Yarın sabah ne yapmalı?

  1. İlk prompt'tan önce repoya kısa bir SECURITY.md koyun ve CLAUDE.md içinden referans verin. Veri sahipliği kuralını en üste yazın.
  2. Kuralları somut yazın. "Güvenli kod yaz" işe yaramıyor, "her query tenant_id ile filtrelenir" yarıyor.
  3. security-guidance plugin'ini kurun, her özellikten sonra /security-review çalıştırın.
  4. Her yeni endpoint için "başka kullanıcının kaydına erişemiyorum" testini modelden isteyin. Test, kuralın gerçekten uygulandığının kanıtı.
  5. Secret taraması ve dependency audit'i CI'da bloklayıcı yapın. Md'ye bırakmayın.
  6. Kural dosyalarındaki değişiklikleri review'dan geçirin.
  7. Mevcut bir projeniz varsa tara-kapat ile başlayın ama orada durmayın. Bulguları kurala çevirip md'ye yazın ki aynı hata bir daha gelmesin.
  8. İş mantığını kendiniz test edin. Negatif tutar, aynı isteği iki kez gönderme, adım atlama. Bunları sizin yerinize kimse düşünmüyor.

Yararlı kaynaklar ve md'ler

Kaynaklar: OWASP Secure Coding with AI Cheat Sheet · OpenSSF: Security-Focused Guide for AI Code Assistant Instructions · Claude Code: Security · Claude Code: security-guidance plugin · anthropics/claude-code-security-review · Cloudflare: How Cloudflare enforces engineering standards using AI · Wiz: secure-rules-files · CSA: Instruction-File Poisoning of AI Coding Agents

Sedat Özdemir

SEDAT ÖZDEMİR

Sr. Expert Pentest & Vulnerability Management Engineer

On yıla yakın ofansif güvenlik deneyimi. Sızma testi, red team, zafiyet yönetimi ve DevSecOps üzerine yazıyor.