Modern web uygulamalarının büyük bir kısmı, kullanıcı bilgilerini saklamak, kimlik doğrulaması yapmak ve dinamik içerik sunmak için ilişkisel veritabanlarıyla iletişim kurar. Bu iletişim, istemciden gelen isteklerin sunucu tarafında işlenip SQL (Structured Query Language) sorgularına dönüştürülmesiyle sağlanır. Ancak kullanıcı girdileri güvenli bir biçimde ele alınmadığında, veritabanı doğrudan saldırganların kontrolüne geçebilir. Web güvenliği dünyasında onlarca yıldır varlığını koruyan en tehlikeli zafiyetlerden biri SQL Injection zafiyetidir.
SQL Injection Nedir ve Mantığı Nasıl Çalışır?
SQL Injection (SQLi), saldırganın bir web uygulaması üzerinden veritabanına giden dinamik SQL sorgularına müdahale etmesine ve istenmeyen komutları çalıştırmasına olanak tanıyan bir güvenlik açığıdır. Zafiyetin temelinde yatan neden, yazılımcının kullanıcıdan gelen veriyi (input) bir veri olarak değil, çalıştırılabilir bir kod parçası gibi algılayarak doğrudan SQL ifadesine eklemesidir.
Normal şartlarda veritabanı yönetim sistemleri (RDBMS), sorgu yapısı (kod) ile sorgunun aradığı değeri (veri) ayırt etmek ister. Eğer kullanıcı girdisi denetlenmeden, filtrelenmeden ya da parametrik yapı kurulmadan sorgu dizesine (string concatenation) eklenirse, saldırgan girdinin içerisine özel SQL karakterleri (örneğin tek tırnak, noktalı virgül veya tire işaretleri) yerleştirerek sorgunun mantığını baştan yazabilir.
Bu zafiyetin başarılı bir şekilde sömürülmesi sonucunda saldırganlar:
- Veritabanındaki hassas verileri (parolalar, kişisel bilgiler, kredi kartları) okuyabilir.
- Veritabanındaki kayıtları silebilir, güncelleyebilir veya yeni kayıtlar ekleyebilir.
- Veritabanı üzerinde yönetimsel işlemler gerçekleştirebilir (tablo silme, kullanıcı yetkilerini artırma).
- Bazı senaryolarda işletim sistemi düzeyinde komut çalıştırarak sunucuyu tamamen ele geçirebilir.
Zafiyetli Bir Senaryo: Giriş Doğrulama Hatası
SQL Injection mantığını anlamanın en kolay yolu, geleneksel bir kullanıcı giriş (login) formunu incelemektir. Aşağıdaki örnekte, kullanıcı adı ve parola alanlarını kontrol eden zafiyetli bir PHP kodu yer almaktadır:
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";
$result = mysqli_query($conn, $query);
Yukarıdaki kod bloğunda, form üzerinden gönderilen $username ve $password değişkenleri doğrudan metin birleştirme yöntemiyle SQL ifadesine dahil edilmiştir. Normal bir kullanıcı bu forma adını ve parolasını girdiğinde sorgu beklenen şekilde çalışır.
Ancak bir saldırgan, kullanıcı adı alanına aşağıdaki girdiyi yazdığında işler değişir:
admin' --
Bu girdi işlendiğinde, arka planda oluşturulan ve veritabanına gönderilen nihai SQL sorgusu şu hali alır:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '...'
Bu örnekte yer alan tek tırnak (') işareti, SQL sorgusundaki kullanıcı adı değerini erken kapatır. Ardından gelen -- (çift tire) ise SQL dilinde satır sonuna kadar olan kısmı açıklama satırı (comment) haline getirir. Sonuç olarak veritabanı motoru, password kontrolünün bulunduğu kısmı tamamen yok sayar ve yalnızca admin kullanıcısının var olup olmadığını kontrol eder. Eğer bu kullanıcı mevcutsa, sistem saldırganı parola girmesine gerek kalmadan yönetici olarak sisteme dahil eder.
SQL Injection Türleri
SQL Injection saldırıları, saldırganın hedef sistemden aldığı geri bildirime ve kullandığı veri çıkarma kanalına göre farklı sınıflara ayrılır:
- In-band SQLi (Klasik): Saldırının gerçekleştirildiği kanal ile sonucun alındığı kanalın aynı olduğu durumdur. En yaygın iki alt türü Hata Tabanlı (Error-Based) ve Birlik Tabanlı (UNION-Based) saldırılardır. Hata tabanlı saldırılarda veritabanı hata mesajları veri sızdırmak için kullanılırken, UNION tabanlı saldırılarda orijinal sorguya eklenen
UNION SELECTifadeleriyle başka tablolardan veri çekilir. - Inferential SQLi (Kör / Blind SQLi): Uygulamanın doğrudan ekrana bir veritabanı çıktısı ya da hata mesajı döndürmediği senaryolardır. Saldırgan, sisteme doğru/yanlış soruları sorarak sayfa içeriğindeki ufak değişimleri (Boolean-based) ya da veritabanına komut vererek yanıt süresindeki gecikmeleri (Time-based, örneğin
pg_sleepveyaWAITFOR DELAY) gözlemler. - Out-of-band SQLi: Sunucunun saldırgana doğrudan yanıt veremediği veya yanıtların çok yavaş olduğu durumlarda, veritabanının dış ağa DNS veya HTTP istekleri göndermesini tetikleyerek verinin bu harici istekler üzerinden dışarı çıkarılması tekniğidir.
SQL Injection Nasıl Önlenir?
SQL Injection saldırılarına karşı korunmak, tesadüfi filtrelemelerle değil, sistemli bir yazılım mimarisiyle mümkündür.
1. Parametreli Sorgular ve Hazırlıklı İfadeler (Prepared Statements)
SQL Injection’a karşı en etkili ve kesin savunma yöntemi parametreli sorguların (Prepared Statements) kullanılmasıdır. Bu yaklaşımda sorgu yapısı ile kullanıcı girdileri veritabanına ayrı kanallardan iletilir. Veritabanı motoru önce sorgunun şablonunu derler, ardından kullanıcı girdisini basit bir değişmez (literal) değer olarak atar. Girdi içerisinde zararlı SQL komutları olsa dahi, bunlar kod olarak derlenmez.
Aşağıda, PHP PDO kullanılarak hazırlanmış güvenli bir sorgu örneği bulunmaktadır:
$stmt = $pdo->prepare('SELECT id, email FROM users WHERE username = :username AND password = :password');
$stmt->execute([
'username' => $username,
'password' => $hashed_password
]);
$user = $stmt->fetch();
Bu kod bloğunda :username ve :password alanları birer yer tutucudur (placeholder). Veritabanı motoru, execute metodu ile gelen değerlerin türü ne olursa olsun bunları sadece bir metin verisi olarak kabul eder; bu sayede tek tırnak gibi özel karakterler mantıksal sorgu akışını bozamaz.
2. Nesne-İlişkisel Eşleme (ORM) Kullanımı
Modern yazılım geliştirme süreçlerinde Entity Framework, Hibernate, Prisma veya Django ORM gibi kütüphaneler yaygın şekilde kullanılır. Bu araçlar arka planda otomatik olarak parametreli sorgular çalıştırır ve geliştiriciyi manuel SQL yazma yükünden kurtarır. Ancak unutulmamalıdır ki, ORM kütüphaneleri içinde dahi “raw query” (ham sorgu) fonksiyonları kullanılarak metin birleştirme yapılırsa açık yeniden ortaya çıkar.
3. Girdi Doğrulama ve Beyaz Liste (Allowlist)
Parametreli sorguların uygulanamadığı dinamik alanlar olabilir. Örneğin, bir sorguda sıralama yapılacak sütun adı (ORDER BY column_name) veya sıralama yönü (ASC / DESC) parametreli sorgularda doğrudan değişken olarak atanamaz.
Bu gibi durumlarda beyaz liste (allowlist) yaklaşımı benimsenmelidir. Kullanıcıdan gelen değer, yalnızca izin verilen değerler kümesiyle karşılaştırılmalı ve eşleşme yoksa işlem reddedilmelidir:
$allowed_columns = ['id', 'username', 'created_at'];
$sort_by = in_array($_GET['sort'], $allowed_columns) ? $_GET['sort'] : 'id';
$allowed_directions = ['ASC', 'DESC'];
$order = in_array(strtoupper($_GET['order']), $allowed_directions) ? strtoupper($_GET['order']) : 'ASC';
$query = "SELECT * FROM products ORDER BY " . $sort_by . " " . $order;
Bu örnekte, dışarıdan gelen sıralama parametreleri katı bir biçimde kontrol edilmekte, listelenen üç sütun ve iki yön dışında hiçbir değer kabul edilmemektedir.
4. En Düşük Yetki İlkesi (Principle of Least Privilege)
Web uygulamasının veritabanı bağlantısında kullanılan kullanıcının yetkileri katı bir şekilde sınırlandırılmalıdır. Bir web uygulamasının veritabanında root, sa ya da DBA düzeyinde tam yetkili bir hesapla oturum açması büyük bir güvenlik riskidir. Uygulama yalnızca ihtiyaç duyduğu tablolarda SELECT, INSERT, UPDATE işlemlerini yapabilecek izne sahip olmalı; tablo silme (DROP), prosedür değiştirme veya dosya sistemine erişim yetkileri kısıtlanmalıdır.
5. Web Uygulama Güvenlik Duvarı (WAF) Katmanı
WAF çözümleri, HTTP trafiğini inceleyerek yaygın SQL Injection desenlerini (payload) tespit edebilir ve sunucuya ulaşmadan engelleyebilir. Ancak WAF araçları birincil bir çözüm değil, yalnızca ek bir savunma katmanı (defense-in-depth) olarak değerlendirilmelidir. Asıl güvenliğin uygulama katmanında sağlandığından emin olunmalıdır.
Özet ve Sonraki Adımlar
SQL Injection, web uygulamalarının mantıksal tasarım hatalarından beslenen ve sonuçları veri tabanı ihlallerine varabilen yüksek riskli bir güvenlik açığıdır. Bu açıktan korunmanın temel kuralı, istemci tarafından iletilen hiçbir veriye güvenmemek ve kullanıcı verisini sorgu mantığından kesin çizgilerle ayırmaktır.
Güvenliğinizi pekiştirmek için mevcut kod tabanınızdaki dinamik SQL birleştirmelerini derhal parametreli sorgularla değiştirin, veritabanı kullanıcı izinlerinizi gözden geçirin ve statik kod analizi (SAST) araçlarını yazılım dağıtım (CI/CD) süreçlerinize entegre edin.


Comments
No comments yet. Be the first to comment.