SQL injection nedir, bir form veritabanını nasıl ele verir?
Bir arama kutusuna yazdığınız şey, çoğu zaman arka planda bir veritabanına sorulan soruya dönüşür. O soru doğru kurulmadıysa, yazdığınız metin bir anda komuta dönüşebilir. Açığın nasıl oluştuğunu, neden yirmi yılı aşkın süredir hâlâ karşımıza çıktığını ve nasıl kapatıldığını açıklıyoruz.
- Güncellendi:
- 6 dk okuma
- 3 kaynak
Kısaca
- SQL injection, kullanıcının yazdığı metnin veritabanı komutuyla karışması sonucu ortaya çıkar. Saldırgan bu yolla olmaması gereken verileri okuyabilir, değiştirebilir, silebilir, bazen sunucunun kendisine bile uzanabilir.
- Çözüm neredeyse tek cümle: kullanıcının yazdığını komutun bir parçası değil, yalnızca veri olarak ele almak. Buna hazır ifade (prepared statement) ya da parametreli sorgu deniyor.
Bir siteye giriş yaptığınızı düşünün. Kullanıcı adınızı ve parolanızı yazıp “Giriş” düğmesine basıyorsunuz. O an arka planda, sitenin veritabanına şöyle bir soru gidiyor: “İçinde bu kullanıcı adı ve bu parola olan bir kayıt var mı?” Veritabanı “evet” derse içeri alınıyorsunuz. Bir e-ticaret sitesinde ürün aradığınızda, bir bankada hesap hareketlerinize baktığınızda, bir haber sitesinde eski yazılara göz attığınızda da benzer sorular gidiyor. Bu sorular, veritabanlarının ortak dili olan SQL ile yazılır.
Sorun şu ki bu soru çoğu zaman, sizin yazdığınız metin olduğu gibi cümlenin içine yapıştırılarak kuruluyor. Yani sizin yazdığınız şey, sorunun kendisiyle aynı yere konuyor. Burada tehlike başlıyor. Eğer siz kullanıcı adı kutusuna sıradan bir isim değil de, o cümlenin anlamını değiştirecek bir şey yazarsanız, veritabanı artık sizin verinizi değil, sizin komutunuzu okur.
Bunu günlük hayattan bir örnekle düşünebilirsiniz. Bir resmî dairede görevliye bir form veriyorsunuz ve görevli formdaki adı okuyarak arşivden dosyanızı getiriyor. Adınızın yazdığı yere “Ahmet Yılmaz, ayrıca bütün dosyaları da getir” yazsanız ve görevli bunu da talimat sanıp yerine getirse, işte SQL injection budur. Görevli formun hangi kısmının veri, hangi kısmının talimat olduğunu ayırt edemiyor.
Küçük ama meşhur bir örnek
Bu anlatılırken herkesin kullandığı klasik bir örnek vardır. Giriş sorusunu kuran kod, parolanın doğru olup olmadığını “şu kullanıcının parolası şuna eşit mi” diye sorar. Saldırgan parola yerine, kabaca “ya da 1’in 1’e eşit olduğu her durumda” anlamına gelen bir ifade yazarsa, soru artık “parola doğruysa ya da zaten her zaman doğru olan bir şey varsa” hâline gelir. Her zaman doğru olan bir koşul eklendiği için veritabanı “evet, bu kayıt uyuyor” der ve kapı açılır. Saldırgan parolayı hiç bilmeden içeri girmiş olur.
Bu yalnızca bir örnek. Aynı mantıkla, bir saldırgan olmaması gereken verileri okuyabilir (başka kullanıcıların bilgileri gibi), kayıtları değiştirebilir, hatta bütün bir tabloyu silebilir. Yani SQL injection “sadece giriş ekranı” meselesi değil. Arama kutusundan sipariş formuna, bir ürün sayfasının adresindeki numaradan bir mobil uygulamanın arka planda gönderdiği isteğe kadar, kullanıcıdan gelen herhangi bir şeyin veritabanına ulaştığı her yerde ortaya çıkabilir.

Kör SQL injection
Her SQL injection sonucunu ekranda göstermez. Bazen site, veritabanından gelen yanıtı hiç göstermez, yalnızca “kayıt bulundu” ya da “bulunamadı” der. Bazen hiçbir şey söylemez. Saldırganlar bu durumda da yollarını bulur. Sorulara evet-hayır cevabı verdirerek ya da veritabanının yanıt süresini ölçerek, verileri harf harf çıkarabilirler. Buna kör (blind) SQL injection deniyor.
Kör türün önemli bir sonucu var: “Hata mesajı göstermiyoruz, öyleyse güvendeyiz” düşüncesi yanlış. Hata mesajlarını gizlemek iyi bir uygulamadır, ama açığı kapatmaz, yalnızca istismarı yavaşlatır. Otomatik araçlar bu yavaşlığı dakikalar içinde telafi edebiliyor.
Zarar nereye kadar gidebilir?
SQL injection’ın etkisi, uygulamanın veritabanına hangi yetkiyle bağlandığına bağlı. En hafif durumda saldırgan, uygulamanın zaten gösterdiği verilerin biraz fazlasını görür. Daha kötü durumlarda bütün kullanıcı tablosunu, parolaların özetlerini, kişisel verileri ve ödeme kayıtlarını indirebilir.
En kötü durumda iş veritabanında da kalmaz. Bazı veritabanı sistemleri, yeterli yetki verildiğinde dosya okuyup yazabiliyor ya da işletim sistemi komutu çalıştırabiliyor. Uygulama veritabanına en yetkili hesapla bağlanıyorsa, bir SQL injection açığı uzaktan kod çalıştırmaya kadar uzanabiliyor.
Bu yüzden SQL injection bir veri ihlali meselesi. Bir formdaki tek bir hata, binlerce kişinin bilgisinin sızmasına yol açabiliyor. Kişisel veriler etkilendiğinde, Türkiye’de olay Kişisel Verileri Koruma Kurumu’na bildirilmesi gereken bir ihlale dönüşüyor.
Neden hâlâ karşımıza çıkıyor?
SQL injection yeni bir şey değil, ilk kez 1998’de kamuoyunda tartışıldı. Buna rağmen hâlâ en sık görülen açıklardan biri. Zafiyet taramalarımızda bunu her gün görüyoruz: Yeni yayımlanan CVE kayıtları arasında, özellikle WordPress eklentilerinde ve küçük açık kaynak projelerde, neredeyse her gün yeni bir SQL injection kaydı çıkıyor.
Bunun birkaç sebebi var. Birincisi, kolayca yapılan bir hataya dayanması: kullanıcının yazdığını, komutun metnine doğrudan eklemek. Bir geliştirici acele ettiğinde ya da eski bir kodu olduğu gibi kullandığında bu hata kolayca araya sızar. İkincisi, bir sitede yüzlerce form ve uç nokta olabilir, bir tanesinin unutulması yeter. Üçüncüsü, eski sistemler. Yıllar önce yazılmış ve kimsenin dokunmaya cesaret edemediği bir kod, bugün hâlâ çalışıyor olabilir.
Son olarak, modern araçlar da sorunu tamamen çözmüyor. Çoğu yazılım çatısı veritabanıyla güvenli konuşmanın yollarını hazır sunuyor, ama geliştiricinin bu yolu bırakıp elle sorgu yazdığı her yerde aynı risk geri geliyor. Yapay zekâ kod asistanlarının ürettiği kodda da bu hatanın tekrarlandığı görülüyor. Bir asistanın yazdığı sorgu, insan yazmış gibi denetlenmeli.
Nasıl kapatılır?
İyi haber şu: Çözümü neredeyse tek cümleyle özetlenebiliyor. Kullanıcının yazdığı şey asla komutun bir parçası olarak görülmemeli, yalnızca veri olarak ele alınmalı. Bunu sağlayan yönteme hazır ifade (prepared statement) ya da parametreli sorgu deniyor.
Bu yöntemde komutun iskeleti önceden, sabit olarak kurulur. “Şu kullanıcı adına ve şu parolaya sahip kaydı getir” cümlesi, kullanıcı adı ve parolanın yerinde boş kutularla birlikte veritabanına gider. Kullanıcının yazdığı ise sonradan, bu kutulara konan bir içerik gibi yerleştirilir. Veritabanı kutunun içindekini hiçbir zaman komut olarak okumaz. Böylece kullanıcı ne yazarsa yazsın, o metin hiçbir zaman komuta dönüşemez. Resmî daire örneğine dönersek: Görevliye “formdaki ad kutusuna ne yazılırsa yazılsın, onu yalnızca bir ad olarak oku” talimatı verilmiş olur.
Bunun yanında birkaç önlem daha işi sağlamlaştırır:
- Gelen veriyi beklenen biçime göre denetlemek. Bir yaş alanına yalnızca sayı, bir tarih alanına yalnızca tarih kabul etmek. Bu tek başına koruma değildir, ama saldırganın hareket alanını daraltır.
- Veritabanına bağlanan hesaba yalnızca ihtiyacı kadar yetki vermek. Sadece okuması gereken bir hesabın silme yetkisi hiç olmamalı. Uygulamanın veritabanına yönetici hesabıyla bağlanması, bir açık çıktığında zararı en üst seviyeye çıkarır.
- Hata mesajlarını kullanıcıya olduğu gibi göstermemek. Veritabanının ayrıntılı hata çıktısı, saldırgana sistemin içini gösteren bir harita gibidir. Hataları kayıtlara yazın, kullanıcıya sade bir mesaj gösterin.
- Hassas verileri ayrıca korumak. Parolalar düz metin olarak değil, güçlü özet algoritmalarıyla saklanmalı. Böylece veriler sızsa bile parolalar doğrudan kullanılamaz.
- Bir web uygulama güvenlik duvarı kullanmak. Bilinen saldırı kalıplarını engelleyebilir, ama bunu bir yamalık bez gibi düşünün. Asıl çözüm kodun kendisinde.
Kendi sitenizde ne yapmalı?
Bir siteniz varsa ve hazır bir içerik yönetim sistemi kullanıyorsanız, en önemli iş çekirdeği ve eklentileri güncel tutmak. Hazır sistemlerdeki SQL injection açıklarının çoğu, yıllardır güncellenmeyen eklentilerden çıkıyor. Kullanmadığınız eklentileri silin, çünkü kapalı bir eklenti bile bazen dışarıdan çağrılabiliyor.
Kendi kodunuzu yazıyorsanız, veritabanına giden her sorguyu bir kez gözden geçirin ve elle birleştirilmiş metinler arayın. Kullandığınız yazılım çatısının veritabanı kütüphanesinin parametreli sorgu desteğini kullanın. Yazılım geliştiren bir ekipseniz, kod incelemelerinde bu kontrolü standart bir madde hâline getirin.
Özetle SQL injection, bilgisayarın “veri” ile “komut”u ayıramamasından doğar. Bu ayrımı en baştan doğru kurmak, yani kullanıcının yazdığını her zaman veri olarak görmek, bu açığın neredeyse tamamını ortadan kaldırır. Aynı ilkenin yapay zekâ dünyasındaki karşılığını prompt injection rehberimizde anlattık. Orada da sorun aynı, ama çözüm henüz bu kadar net değil.