İçeriğe atla
0 XP 0
← Blog
Güvenlik

SQL Injection Nedir? Örnekle Anlatım ve Korunma

SQL injection, kullanıcının gönderdiği verinin uygulamanın veritabanı sorgusuna kod olarak karışmasıyla oluşan bir web güvenlik açığıdır. Saldırgan bu yolla veri okuyabilir, değiştirebilir ya da silebilir. Kökü, sorgunun metin birleştirilerek kurulmasıdır. En etkili korunma yolu parametreli sorgudur; en az yetki, izin listesi ve WAF ek katman olarak çalışır.

Yazan: Enes DenizSon güncelleme: 8 dk okuma

Bir giriş formu, bir arama kutusu ya da adres çubuğundaki bir sayı. Uygulama bu değeri veritabanına sorarken sorgu metnine yapıştırıyorsa kapı aralıktır. Bu yazıda SQL injection'ın nasıl doğduğunu, türlerini ve gerçek vakaları göreceksin. En önemlisi, kodda nasıl kapatıldığını öğreneceksin. Örnekler savunma gözüyle yazıldı: zafiyetli desen ve düzeltilmiş hâli yan yana.

SQL injection nedir?

SQL injection, kullanıcıdan gelen verinin SQL sorgusunun kod kısmına karışmasıyla ortaya çıkan bir enjeksiyon açığıdır. Veritabanı veriyle komutu ayırt edemez. Saldırganın yazdığı parça, sorgunun bir parçası gibi çalışır. Türkçede "SQL enjeksiyonu" ya da kısaca SQLi de denir.

MITRE bu açığı CWE-89 koduyla tanımlar. OWASP Top 10 2025 listesinde Enjeksiyon kategorisi A05 sırasındadır. OWASP'a göre bu kategoriye bağlı 62.445 CVE kaydı var. Bunların 14 binden fazlası doğrudan SQL injection'dır.

MITRE'nin Aralık 2025'te yayımladığı CWE Top 25 listesinde SQL injection ikinci sıraya çıktı. Liste, Haziran 2024 ile Haziran 2025 arasındaki 39.080 CVE kaydından hesaplandı. Açığın ilk kamuya açık anlatımı ise 1998'e uzanır. Phrack dergisinin 54. sayısında rain.forest.puppy takma adlı araştırmacı sorunu ayrıntılı anlattı. Sorun çeyrek asırlık, ama hâlâ yazılıyor.

SQL injection nasıl oluşur?

SQL injection, uygulama sorguyu metin birleştirerek kurduğunda oluşur. Geliştirici kullanıcı girdisini doğrudan sorgu dizgesine ekler. Veritabanı sürücüsü eline tek bir metin alır. Hangi kısmın veri, hangisinin komut olduğunu bilemez.

Aşağıdaki Python fonksiyonu bu hatayı gösterir:

# Zafiyetli desen: girdi sorgu metnine yapıştırılıyor
def kullanici_bul(db, eposta):
    sorgu = f"SELECT id, ad FROM kullanicilar WHERE eposta = '{eposta}'"
    return db.execute(sorgu).fetchone()

Fonksiyon normal e-posta adresleriyle sorunsuz çalışır. Testler geçer, kod incelemesinde göze batmaz. Sorun, girdinin içinde tırnak ya da SQL söz dizimi olduğunda başlar. O an girdi sorgunun mantığını değiştirebilir. Koşulu etkisiz kılar, başka tabloya uzanır ya da veriyi siler.

Açık yalnız giriş formlarında çıkmaz. PortSwigger'ın Web Security Academy notlarına göre SQL injection en çok WHERE koşulunda görülür. Ama UPDATE ve INSERT değerlerinde, tablo ve sütun adlarında, ORDER BY kısmında da çıkar. Çerez, HTTP başlığı, JSON gövdesi ve dosya adı da birer girdidir.

SQL injection türleri nelerdir?

SQL injection, saldırganın sonucu nasıl gördüğüne göre üç ana türe ayrılır: in-band, kör (blind) ve out-of-band. Türlerin kökü aynıdır, dolayısıyla savunma da aynıdır. Ama savunmacı için bıraktıkları iz farklıdır.

TürSaldırgan sonucu nasıl görür?Savunmacının göreceği iz
In-band: hata tabanlıVeritabanı hata metni sayfaya yansır.Loglarda art arda söz dizimi hataları.
In-band: UNION tabanlıEk sorgunun sonucu normal yanıtın içinde döner.Aynı uç noktaya kısa aralıkla gelen, giderek değişen istekler.
Kör: mantıksalDoğru ve yanlış koşulda sayfa farklı görünür.Yanıt boyutunda küçük ama düzenli farklar.
Kör: zaman tabanlıYanıtın gecikip gecikmediğine bakar.Belirli isteklerde açıklanamayan gecikme.
Out-of-bandVeritabanı dışarıya DNS ya da HTTP isteği atar.Veritabanı sunucusundan beklenmedik dış bağlantı.
İkinci dereceGirdi önce güvenle saklanır, sonra başka sorguda birleştirilir.Açık, girdinin alındığı yerden farklı bir ekranda tetiklenir.

Son satır sık düşülen bir tuzağı gösterir. Geliştirici veritabanından okuduğu veriyi "zaten bizim veri" sayar. PortSwigger bu türü ikinci derece (second-order) SQL injection olarak anlatır. Kural basittir: veri nereden gelirse gelsin, sorguya parametreyle girer.

Out-of-band türü, veritabanı sunucusunun dışarıya bağlantı açabilmesine dayanır. Veritabanının internete çıkışını ağ düzeyinde kapatmak bu yolu keser.

Gerçek hayatta SQL injection ne kadar zarar verdi?

SQL injection, son yılların en büyük toplu veri hırsızlıklarından birinin başlangıç noktasıydı. 2023'teki MOVEit Transfer vakası bunun en çarpıcı örneğidir.

CISA ve FBI'ın ortak uyarısına (AA23-158A) göre Cl0p fidye yazılımı çetesi 27 Mayıs 2023'te saldırıya başladı. Hedef, Progress Software'in dosya aktarım ürünü MOVEit Transfer'dı. Çete o güne kadar bilinmeyen bir SQL injection açığını kullandı: CVE-2023-34362. İnternete açık sunuculara LEMURLOOT adlı bir web kabuğu yerleşti. Ardından veritabanlarındaki dosyalar çalındı. Emsisoft'un 28 Haziran 2024 tarihli derlemesine göre etkilenen kurum sayısı 2.773'e, kişi sayısı yaklaşık 95,8 milyona ulaştı.

MOVEit tek örnek değil. İngiliz telekom şirketi TalkTalk, Ekim 2015'te eski web sayfalarındaki SQL injection açıkları yüzünden saldırıya uğradı. İngiltere'nin veri koruma otoritesi ICO'ya göre 156.959 müşterinin verisine erişildi. ICO, Ekim 2016'da şirkete 400 bin sterlin ceza kesti. Gerekçelerden biri, yaması yıllardır hazır olan yazılımın güncellenmemesiydi.

CISA ve FBI, MOVEit'in ardından Mart 2024'te yazılım üreticilerine özel bir "Secure by Design" uyarısı yayımladı. Uyarıya göre SQL injection en az 2007'den beri "affedilemez" açıklar arasında sayılıyor. MySQL, bu sınıfı ortadan kaldıran hazırlanmış ifadeleri 2004'te getirdi. Yani çözüm yirmi yıldır elimizde.

Türkiye'de bir SQL injection kişisel veri sızıntısına yol açarsa iş teknik sorun olmaktan çıkar. Veri sorumlusu ihlali KVKK'ya bildirmek zorunda kalır. Süreç için KVKK veri ihlali bildirimi yazımıza bakabilirsin.

SQL injection nasıl önlenir?

SQL injection'ı önlemenin temel yolu parametreli sorgudur: sorgu metni ve veri veritabanına ayrı ayrı gider. Girdi ne içerirse içersin yalnız değer olarak yorumlanır. OWASP SQL Injection Prevention Cheat Sheet de birinci savunma olarak bunu sayar.

SQL injection karşılaştırması: sorguya yapıştırılan girdi komut olarak çalışır, parametreli sorguda girdi ayrı gider ve yalnız veri olarak okunur.
SQL injection, girdi sorgu metnine yapıştırıldığında doğar: veritabanı girdiyi komutun parçası sayar. Parametreli sorguda sorgu kalıbı ve değer ayrı gönderilir, böylece girdi ne içerirse içersin yalnız veri olarak okunur.

Yukarıdaki fonksiyonun güvenli hâli şöyle:

# Güvenli desen: yer tutucu ve ayrı parametre
def kullanici_bul(db, eposta):
    sorgu = "SELECT id, ad FROM kullanicilar WHERE eposta = ?"
    return db.execute(sorgu, (eposta,)).fetchone()

Python'un sqlite3 belgeleri bu konuda nettir: sorguyu Python'un metin işlemleriyle kurma. Yer tutucu biçimi sürücüye göre değişir. sqlite3 soru işareti kullanır, psycopg gibi PostgreSQL sürücüleri %s kullanır. Mantık aynıdır.

PHP'de aynı iş PDO ile yapılır:

<?php
// Güvenli desen: PDO ile hazırlanmış ifade
$pdo = new PDO($dsn, $dbKullanici, $dbParola, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stmt = $pdo->prepare('SELECT id, ad, fiyat FROM urunler WHERE kategori_id = :kategori');
$stmt->execute(['kategori' => $kategoriId]);
$urunler = $stmt->fetchAll();

PHP kılavuzu iki noktanın altını çizer. Parametreleri tırnaklamana gerek yoktur, bunu sürücü yapar. Ama sorgunun başka bir kısmı kaçışsız girdiyle kurulursa açık yine doğar. Yer tutucu, bir değerin tamamının yerine geçer; tırnak içine konmaz.

Parametre alamayan yerlerde ne yapmalı?

Tablo adı, sütun adı ve sıralama yönü parametreyle bağlanamaz. Bu yerlerde izin listesi kullanılır. Kullanıcıdan gelen değer koddaki sabit bir listeyle eşleştirilir.

SIRALAMA_SUTUNLARI = {"ad": "ad", "fiyat": "fiyat", "yeni": "olusturma_tarihi"}
def urunleri_sirala(db, alan, artan):
    sutun = SIRALAMA_SUTUNLARI.get(alan, "ad")
    yon = "ASC" if artan else "DESC"
    sorgu = f"SELECT id, ad, fiyat FROM urunler ORDER BY {sutun} {yon}"
    return db.execute(sorgu).fetchall()

Burada f-string var ama kullanıcı girdisi sorguya hiç girmiyor. Sorguya giren her parça koddaki sabit sözlükten gelir. Listede olmayan değer varsayılana düşer.

ORM kullanırken nereye dikkat etmeli?

ORM'ler sorguyu çoğunlukla parametreli kurar, ama ham sorgu kapıları açığı geri getirir. Django belgeleri, QuerySet'lerin parametreleme sayesinde SQL injection'a karşı korunduğunu yazar. Aynı belge raw(), extra() ve RawSQL için ayrıca dikkat ister.

# Riskli: ham sorgu kapısında metin birleştirme
Kullanici.objects.raw(f"SELECT * FROM app_kullanici WHERE ad = '{ad}'")
# Güvenli: ham sorguda da parametre listesi
Kullanici.objects.raw("SELECT * FROM app_kullanici WHERE ad = %s", [ad])

Aynı durum Sequelize, Hibernate ya da Entity Framework için de geçerli. Kod incelemesinde ham sorgu fonksiyonu ile metin birleştirme yan yana geçiyorsa dur ve bak.

En az yetkili veritabanı hesabı neden önemli?

En az yetki açığı kapatmaz, ama bir açığın verebileceği zararı küçültür. Web uygulamasının veritabanı hesabı yönetici olmamalı. Yalnız ihtiyaç duyduğu tablolarda, ihtiyaç duyduğu işlemleri yapabilmeli.

-- Uygulama için ayrı ve dar yetkili rol (PostgreSQL)
CREATE ROLE magaza_uygulama LOGIN;
GRANT SELECT ON urunler TO magaza_uygulama;
GRANT SELECT, INSERT, UPDATE ON siparisler TO magaza_uygulama;
-- Tablo silme, şema değiştirme ve dosya yetkisi yok

Raporlama ekranları için salt okunur ayrı bir hesap aç. Parola tablosuna yalnız kimlik servisinin hesabı erişsin. OWASP, saklı yordamların bazı ortamlarda yüksek yetki isteyebildiği uyarısını da yapar.

Girdi doğrulama ve WAF ne işe yarar?

Girdi doğrulama ve WAF ikinci hattır, parametreli sorgunun yerini tutmaz. CISA'nın 2024 uyarısı bunu açıkça yazar. Girdi temizleme bazı saldırıları durdurur, ama kırılgandır ve çoğu zaman atlatılır.

İzin listesiyle doğrulama yine de değerlidir. Ürün numarası sayı olmalıysa sayıya çevir, değilse reddet. Posta kodu, tarih ve e-posta için biçim kuralı koy. Kara listeyle tek tırnak avlamak ise zayıf bir yöntemdir.

WAF, yani web uygulama güvenlik duvarı, bilinen kalıpları yakalar ve toplu taramayı yavaşlatır. OWASP Core Rule Set bunun yaygın bir örneğidir. Ama kural seti bilinen kalıplara bakar. Kodlanmış ya da yeni bir teknik kuralın etrafından dolaşabilir. WAF'ı, açığı yamalarken zaman kazandıran bir perde gibi düşün.

Hata yönetimini de unutma. Kullanıcıya veritabanı hata metni gösterme, ayrıntıyı yalnız sunucu loguna yaz. Hata tabanlı SQL injection bu ayrıntıyla beslenir. OWASP Top 10 2025'e yeni giren A10 kategorisi, istisnai durumların yanlış yönetimi, tam bu konuya bakar.

Hangi savunma katmanı neyi durdurur?

Tek bir katman yetmez, çünkü her katman farklı bir şeyi durdurur. Aşağıdaki tablo katmanların güçlü ve zayıf yanlarını yan yana koyar.

Savunma katmanıNeyi durdurur?Neyi durdurmaz?
Parametreli sorguDeğer alanlarındaki enjeksiyonu kökten keser.Tablo adı ve ORDER BY gibi parametre alamayan yerleri.
İzin listesiParametre alamayan yerlere keyfi değer girmesini.Listeye eklenmeyi unutulmuş bir alanı.
ORM'nin standart API'siSıradan sorgulardaki enjeksiyonu.Ham sorgu kapılarında metin birleştirmeyi.
En az yetkili hesapTablo silme, şema değiştirme gibi geniş zararı.Hesabın zaten okuyabildiği verinin sızmasını.
Girdi doğrulamaBiçim dışı veriyi erken reddeder.Geçerli biçimdeki zararlı metni.
WAFBilinen kalıpları ve toplu taramayı.Kodlanmış ya da yeni teknikleri.
Sade hata mesajıHata tabanlı bilgi sızıntısını.Kör ve zaman tabanlı teknikleri.
Çıkış trafiği kısıtıOut-of-band veri kaçırmayı.In-band ve kör teknikleri.
Log ve alarmSaldırının fark edilmeden sürmesini.Saldırının kendisini.

Kendi kodunda SQL injection'ı nasıl yakalarsın?

Kendi kodunda SQL injection'ı en hızlı, sorgu kuran her satırı metin birleştirme açısından tarayarak yakalarsın. Sonra bunu otomatik araçlarla desteklersin. Pratik bir sıra şöyle:

  1. Koddaki tüm sorgu noktalarını çıkar: execute, query, raw ve prepare geçen satırlar.
  2. Her noktada sorgu metninin bir değişkenle birleştirilip birleştirilmediğine bak.
  3. Birleştirme varsa parametreli sorguya çevir, parametre alamayan yerde izin listesi kur.
  4. Semgrep ya da CodeQL gibi bir SAST aracını CI hattına ekle.
  5. Uygulama hesabının veritabanı yetkilerini gözden geçir ve daralt.
  6. Loglarda söz dizimi hatası artışı ve olağan dışı yanıt süresi için alarm kur.

CISA da üreticilere aynı yolu önerir. Güvenli yol kütüphanelerde varsayılan olmalı, pull request aşamasında da denetlenmeli. Konu Güvenli Yazılım Geliştirme yolundaki parametreli sorgu dersinde adım adım işlenir.

SQL injection'ı nerede güvenle öğrenirsin?

SQL injection'ı yalnız sana ait ya da test izni verilmiş sistemlerde öğrenebilirsin. Başkasının sitesinde "sadece denemek" de suçtur. İzinsiz erişim ve veri değiştirme, TCK'nın bilişim suçları bölümüne (md. 243-245) girer.

AltaySec Akademi'deki Web Uygulama Güvenliği yolu, enjeksiyon ailesini kök nedenden başlayarak anlatır. SQL enjeksiyonu dersi sorgu mantığını savunmacı gözüyle çözer. Ardından Ayrıştırıcıyı Yanıltmak labında izinli bir hedefte pratik yaparsın. Kategorinin geniş haritası için OWASP Top 10 2025 yazımıza göz at. Sıradaki adım olarak tarayıcı tarafındaki enjeksiyonu, yani XSS'i öğren.

Kaynaklar

Sık sorulan sorular

SQL injection hâlâ yaygın bir açık mı?

Evet. MITRE'nin 2025 CWE Top 25 listesinde SQL injection ikinci sırada. OWASP Top 10 2025'te enjeksiyon kategorisi A05 sırasında ve 14 binden fazla SQL injection CVE kaydı taşıyor. HackerOne ise 2025 raporunda bug bounty programlarında SQL injection bildirimlerinin azaldığını yazıyor. Yani açık eskisi kadar kolay bulunmuyor, ama ürünlerde yazılmaya devam ediyor.

ORM kullanmak SQL injection'ı tamamen önler mi?

Hayır, ama riski belirgin biçimde azaltır. Django, Hibernate ya da Entity Framework gibi ORM'ler standart sorguları parametreli kurar. Sorun, ham sorgu yazmana yarayan kapılarda başlar. raw() ya da benzeri bir fonksiyona kullanıcı girdisini metin birleştirerek verirsen açık geri gelir. Ham sorgu yazman gerekiyorsa orada da parametre listesi kullan.

WAF kullanırsam kodu düzeltmeme gerek kalır mı?

Kalmaz. WAF bilinen saldırı kalıplarını yakalar ve otomatik taramayı yavaşlatır, ama kodlanmış ya da yeni tekniklerle atlatılabilir. CISA ve FBI'ın 2024 uyarısı da girdi temizlemenin kırılgan olduğunu, kalıcı çözümün parametreli sorgu olduğunu söyler. WAF'ı, yama hazırlanırken zaman kazandıran geçici bir perde gibi kullan ve asıl düzeltmeyi kodda yap.

Saklı yordam (stored procedure) kullanmak yeterli mi?

Tek başına garanti değildir. OWASP, güvenli yazılmış saklı yordamları parametreli sorgu kadar etkili sayar. Ama yordamın içinde dinamik SQL metin birleştirmeyle kuruluyorsa açık yordamın içine taşınır. Ayrıca bazı ortamlarda yordam çalıştırmak yüksek veritabanı yetkisi isteyebilir. Yordamı parametreyle çağır ve içinde dinamik birleştirme yapma.

NoSQL veritabanlarında da enjeksiyon olur mu?

Olur. MongoDB gibi veritabanları SQL kullanmaz, ama sorgu yine kullanıcı girdisiyle kurulur. Girdi düz metin yerine sorgu operatörü taşıyan bir nesne olarak gelirse sorgunun mantığı değişebilir. Savunma benzerdir: girdinin türünü doğrula, sorgu nesnesini kodda kur ve kullanıcıdan gelen alanları hiçbir zaman operatör olarak yorumlama.

SQL injection'ı yasal olarak nerede deneyebilirim?

Yalnız kendi kurduğun ortamda, eğitim lablarında ya da kapsamı açıkça izin veren bug bounty programlarında. Rastgele bir sitede tırnak işareti denemek bile izinsiz erişim girişimi sayılabilir ve TCK'nın bilişim suçları maddelerine girer. AltaySec Akademi'nin web lablarını ya da PortSwigger Web Security Academy'yi ücretsiz kullanabilirsin.

  • sql injection nedir
  • sql enjeksiyonu
  • sql injection nasıl önlenir
  • web güvenliği
  • owasp

İlgili yazılar

Okuduğunu uygula.
Yollarda ders ders ilerle, alıştırmalarla pekiştir.
Başla