Girdi güvenilmezdir
- Enjeksiyonun ortak kök nedenini, SQL enjeksiyonu ile XSS'i, parametreli sorguyu, çıktı kodlamayı ve CSP savunmasını çözümlü örneklerle öğrenirsin.
- Web açıklarının çoğu tek cümleye iner: uygulama kullanıcının yazdığına inandı.
- Aynı hata iki büyük aileyi doğurur. Veri sorguya karışırsa SQL enjeksiyonu, sayfaya karışırsa XSS çıkar.
- Çözüm girdiyi temizlemek değil, veriyi koddan ayrı tutmaktır.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +55 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
Web açıklarının çoğu tek cümleye iner: uygulama kullanıcının yazdığına inandı. Kullanıcıdan gelen her şey girdidir. Form alanı, URL parametresi, çerez, başlık, yüklenen dosyanın adı. Hepsini bir saldırgan istediği gibi yazar.
Girdiye peşinen güvenmek en yaygın açık kaynağıdır. Bu ders iki büyük aileyi birlikte ele alır: veri bir sorguya karışınca doğan SQL enjeksiyonu ve veri bir sayfaya karışınca doğan XSS. İkisinin kök nedeni aynıdır. Bu yüzden savunmaları da aynı fikri paylaşır: veriyi koddan ayrı tut.
Ortak kök neden: veri ile kod aynı kanalda
Tehlike, kullanıcı verisinin bir yorumlayıcıya metin olarak yapıştırılmasında doğar. Yorumlayıcı, verilen metni okuyup çalıştıran her katmandır: veritabanı motoru, tarayıcının HTML ayrıştırıcısı, işletim sisteminin kabuğu, LDAP dizin sunucusu, bir şablon motoru. Yorumlayıcı metni okur ve içinde komut görürse onu çalıştırır.
Verinin nerede bittiğini, komutun nerede başladığını ayırt edemez, çünkü ikisi aynı dizede birleşmiştir. OWASP Top 10 2025 bu aileyi A05 Enjeksiyon başlığında toplar ve altına 37 ayrı zayıflık türü bağlar.
Aile geniştir. Veri bir SQL sorgusuna karışırsa SQL enjeksiyonu olur. Bir işletim sistemi komutuna karışırsa komut enjeksiyonu doğar. Bir LDAP filtresine karışırsa kimlik doğrulama atlatılabilir.
Sunucudaki bir şablona karışırsa şablon enjeksiyonu (SSTI) çıkar ve çoğu zaman sunucuda kod çalıştırmaya kadar gider. Tarayıcı tarafında ise aynı kök neden XSS'i doğurur. Adları farklı olsa da hepsi tek bir cümlenin varyasyonudur: güvenilmez veri, komut olarak yorumlanan bir kanala metin gibi girdi. Bir zayıflığı bu gözle okursan, savunmasını da tahmin edersin.
MITRE'nin 2025 CWE Top 25 listesi bu ailenin ağırlığını sayılarla gösterir. Birinci sırada XSS (CWE-79), ikinci sırada SQL enjeksiyonu (CWE-89), dokuzuncu sırada işletim sistemi komut enjeksiyonu (CWE-78) durur. OWASP'ın 2025 verisinde de XSS 30 binden, SQL enjeksiyonu 14 binden fazla CVE kaydıyla eşleşir. Yani en çok bildirilen zayıflıkların tepesi, tek bir tasarım hatasının değişik yüzleridir.
İyi haber şu: kök neden ortak olduğu için savunma da öğrenilebilir bir kalıba iner. Önce SQL enjeksiyonuna, sonra XSS'e yakından bak.
Etkinlik 1 / 6
Aşağıdaki durumları doğru enjeksiyon ailesine yerleştir.
Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.
0/5 seçenek yerleşti.
SQL enjeksiyonu nasıl doğar
SQL enjeksiyonunu bir örnekle gör. Uygulama giriş sorgusunu metin birleştirerek kuruyor ve kullanıcı adını tek tırnaklar arasına doğrudan yapıştırıyor. Kullanıcı adı alanına tırnak ve bir mantık ifadesi yazılırsa, sorgunun anlamı değişir. Aşağıdaki kutuda önce kırılgan kodu, sonra alana yazılan girdiyi, en altta veritabanına giden nihai sorguyu görüyorsun.
Koşul her satırda doğru olduğu için sorgu bütün kullanıcıları döndürür. Uygulama gelen ilk satırı giriş yapan kişi sayarsa, oturum parolasız açılır. Bunu yalnız kendi kurduğun lab ortamında ya da yazılı iznin olan bir hedefte dene.
# Kırılgan kod (Python) sorgu = "SELECT id FROM kullanici WHERE ad = '" + ad + "'" # ad alanına yazılan girdi ' OR '1'='1 # Veritabanına giden nihai sorgu SELECT id FROM kullanici WHERE ad = '' OR '1'='1'
Anlamın nasıl kaydığını izle. Kullanıcının yazdığı tek tırnak, ad değerini erkenden kapatır. Ardından gelen OR '1'='1' her satır için doğru olan bir koşul ekler. Böylece WHERE artık kimseyi süzmez ve bütün tablo sonuca girer.
Buradaki asıl sorun tırnak karakteri değil, verinin sorgu metnine kod gibi girmesidir. Tırnağı yasaklasan başka bir yol bulunur. Çünkü hata girdide değil, verinin kodla aynı kanalda taşınmasındadır. Kodu okurken bu yüzden tek bir şeye bak: sorgu metni kullanıcı verisiyle birleştiriliyor mu?
Enjeksiyon her zaman bu kadar görünür olmaz. Bazen uygulama hata basmaz, yalnız davranışı değişir. Doğru koşulda sayfa yüklenir, yanlış koşulda boş döner. Koşul değiştikçe yanıtın nasıl değiştiği okunarak veri parça parça çıkarılabilir; buna kör enjeksiyon (blind injection) denir.
Kimi zaman fark yanıtın süresindedir: yanıt belirgin biçimde gecikirse koşul doğrudur. Test araçları bu farkları ölçerek otomatikleştirir. Savunma açısından önemli olan şu: hata mesajını gizlemek enjeksiyonu kapatmaz, yalnız görünürlüğü azaltır.
Derinleş:Enjeksiyonun kısa tarihi
SQL enjeksiyonu ilk kez 1998'de bir güvenlik dergisinde ayrıntılı anlatıldı. O yıllarda web uygulamaları hızla yayılıyordu ve sorguları elle birleştirmek yaygın alışkanlıktı. Kavram kültüre de girdi.
2007'de yayımlanan bir çizgi bant, oğlunun adını Robert'); DROP TABLE Students;-- koyan bir anneyi anlatır. Okul arayıp o yılın öğrenci kayıtlarını kaybettiğini söyler. Şaka, tam da bu dersin özünü gösterir: ad alanına yazılan metin, veri kanalında kalmayıp komut olarak yorumlanırsa tablo bile silinebilir.
Annenin son sözü de savunmayı özetler: umarım veritabanı girdilerini temizlemeyi öğrenmişsinizdir. Bugün bunun doğru yolu karakter süzmek değil, parametreli sorgudur: veri ayrı kanaldan gider. Aradan geçen yıllara rağmen kök neden değişmedi.
Etkinlik 2 / 6
Hangi satır enjeksiyonu doğuruyor?
Parametreli sorgu ve tespit
Doğru çözümün adı parametreli sorgudur; hazır ifade (prepared statement) de denir. Sorgunun iskeletini önce veritabanına gönderirsin ve değerin geleceği yere bir yer tutucu koyarsın. Kullanıcı verisini ayrı kanaldan, parametre olarak yollarsın. Veritabanı iskeleti bir kez derler ve gelen değeri artık kod değil, saf veri olarak işler.
Aşağıda aynı sorgunun güvenli hâli var. Girdinin içinde tırnak, noktalı virgül ya da yorum işareti olması artık fark etmez; değer, aranacak metnin kendisi sayılır. OWASP birincil savunma olarak bunu önerir.
# Güvenli kod (Python, DB-API) sorgu = "SELECT id FROM kullanici WHERE ad = ?" satir = baglanti.execute(sorgu, (ad,)).fetchone() # ad alanına "' OR '1'='1" yazılsa bile # veritabanı bunu tek bir metin değeri olarak arar, # koşul olarak yorumlamaz.
Aynı mantık başka kanallar için de işler. Bir kabuk komutuna kullanıcı verisi ekleyeceksen, komutu tek bir dizede kurup kabuğa verme. Bunun yerine programı ve argümanlarını ayrı bir liste olarak çağır; işletim sistemi argümanı komut olarak değil, veri olarak alır. Bazı yerler ise parametre kabul etmez.
Tablo adı ya da sıralama sütunu sorgu iskeletinin parçasıdır ve yer tutucuyla verilemez. Orada tek çözüm izin listesidir (allowlist): kabul edilen değerleri sen yazarsın, kullanıcı yalnız aralarından seçer. Ek katman olarak veritabanı kullanıcısının yetkisini de en aza indir.
Savunmayı kurduktan sonra izlemeyi bırakma, çünkü enjeksiyon denemeleri loglarda iz bırakır. Aynı kaynaktan kısa sürede gelen, parametre değerinde tek tırnak, yorum işareti ya da SQL anahtar kelimesi taşıyan istekler dikkat ister. Tek tırnağın hemen ardından gelen 500 yanıtı daha güçlü bir işarettir, çünkü girdinin sorguyu bozduğunu gösterir.
Veritabanı tarafında sözdizimi hatalarının ani artışı da aynı şeyi anlatır. Web uygulama güvenlik duvarı bilinen desenleri keser, ama kök nedeni kapatmaz. Bir bulgu gördüğünde kodda sorguyu kuran satırı bul ve parametreli hâle getir.
Etkinlik 3 / 6
SQL enjeksiyonuna karşı birincil savunma hangisidir?
XSS: veri sayfaya karışınca
İkinci aile tarayıcı tarafındadır. Siteler arası betik çalıştırma, kısaca XSS, saldırganın yazdığı metnin sayfaya kod olarak girmesidir. Yorum alanına bir betik etiketi yazılır, uygulama onu olduğu gibi sayfaya basar ve o kod başka kullanıcının tarayıcısında çalışır.
Etkisi küçümsenmez, çünkü kod kurbanın oturumunda çalışır. Kurbanın gördüğü sayfayı değiştirir, formları kurban adına gönderir, HttpOnly değilse oturum çerezini okur. Yönetici paneline kalıcı bir XSS düşerse, o paneli açan her yöneticinin tarayıcısında çalışır.
XSS üç biçimde görülür. Yansıyan (reflected) tipte kod bir istekle gelir ve aynı yanıtta geri basılır; kurban çoğu zaman hazırlanmış bir bağlantıya tıklayarak tetikler. Saklı (stored) tipte kod veritabanına yazılır ve o içeriği açan her ziyaretçide çalışır; en tehlikeli biçim budur.
DOM tabanlı tipte sunucu veriyi yanıta basmaz; sayfadaki JavaScript, adres çubuğundaki ya da başka bir kaynaktaki veriyi alıp tehlikeli bir yere kendisi yazar. Aşağıdaki örnekte bir arama sayfası, arama kelimesini yanıta hiç işlemeden basıyor.
# Kırılgan: arama kelimesi doğrudan HTML'e giriyor yanit = "<p>Arama sonucu: " + arama + "</p>" # arama alanına yazılan zararsız görünen girdi <b onmouseover=alert(1)>test</b> # Tarayıcının aldığı HTML: etiket artık canlı <p>Arama sonucu: <b onmouseover=alert(1)>test</b></p>
Sorunu gör: uygulama küçüktür işaretini ve öznitelikleri metin sanıyor, tarayıcı ise canlı HTML olarak yorumluyor. Savunmanın adı çıktı kodlamadır (output encoding). Veriyi yazacağın bağlama göre kodlarsın. HTML gövdesinde küçüktür, büyüktür ve tırnak işaretleri < gibi güvenli karşılıklarına çevrilir; tarayıcı onları metin olarak gösterir, etiket olarak değil.
Öznitelik içinde, JavaScript bloğunda ve URL'de kurallar farklıdır, bu yüzden kodlamayı bağlam belirler. İyi haber şu: modern çatılar bu işi varsayılan yapar. React ve Vue, sayfaya bastığın metni kendiliğinden kodlar.
Tehlike, o korumayı elle kapattığın yerde doğar. React'in ham HTML basan dangerouslySetInnerHTML özelliğini yalnız güvendiğin, kaynağı kontrol edilmiş veriyle kullan. Şablon motorunun otomatik kaçırma davranışını kapatma.
DOM tabanlı XSS'te aynı hata tarayıcıda olur: sayfadaki betik adres çubuğundaki bir değeri alıp innerHTML ile yazar. Düzeltme, metni textContent ile yazmaktır; tarayıcı onu etiket olarak değil, düz metin olarak gösterir. Kullanıcı içeriğinin biçimli görünmesi gerekiyorsa, onu güvenli bir arındırıcıdan geçir: izin listesindeki etiketler kalsın, betik ve olay öznitelikleri düşsün.
Derinleş:Bir solucanın bıraktığı ders
Saklı XSS'in gücünü 2005'te bir sosyal ağda yaşanan olay iyi anlatır. Genç bir kullanıcı, profil sayfasına kısa bir betik yerleştirdi.
Profili görüntüleyen her ziyaretçinin tarayıcısı betiği çalıştırıyor, onu ziyaretçinin kendi profiline kopyalıyor ve yazara arkadaşlık isteği yolluyordu. Böylece kod, profilden profile kendi kendine yayıldı ve yirmi saat içinde bir milyonu aşkın hesaba ulaştı. Zarar veri çalmak değildi, ama olay saklı XSS'in ölçeğini kanıtladı: tek bir kayıt, onu gören herkeste çalışır.
Ders iki yönlüdür. Kullanıcı içeriğini sayfaya basarken bağlamına göre kodla. Anormal davranışı da izle: kısa sürede aynı içeriği kopyalayan çok sayıda hesap, yayılan bir XSS'in izidir. Kullanıcı içeriğini gösteren her alan, kendi başına küçük bir güven sınırıdır.
Etkinlik 4 / 6
Yorum alanına yazılmış satırlardan XSS denemesi barındıranları yakalayan bir desen yaz.
Yazdıkça eşleşen satırlar işaretlenir. Hedef: yalnız doğru satırlar eşleşsin.
Katmanlı savunma ve CSP
Tek bir önleme güvenme, savunmayı katmanla. Birinci katman girdi doğrulamadır: her alanı beklenen biçime göre denetle. Yaş bir sayıdır, e-posta belli bir kalıba uyar, ülke kodu iki harftir. İzin listesiyle doğrulama, yasak liste denemesinden her zaman daha sağlamdır.
İkinci katman az önce gördüğün çıktı kodlamadır. Üçüncü katman içerik güvenlik politikasıdır (Content-Security-Policy, CSP): tarayıcıya sayfanın hangi kaynaklardan betik çalıştırabileceğini söyleyen bir yanıt başlığı. Dördüncü katman oturum çerezine HttpOnly yazmaktır; XSS çalışsa bile betik oturumu okuyamaz.
CSP'nin değeri şudur: bir XSS açığı gözden kaçsa bile etkisini sınırlar. İyi kurulmuş bir politika satır içi betiği ve bilinmeyen kaynaklardan gelen betiği reddeder. Böylece sayfaya sızan kodu tarayıcı çalıştırmadan engeller.
Aşağıdaki başlık yalnız sitenin kendi kaynağındaki betiği kabul eder, satır içi betiği kapatır ve nesne gömülmesini tümden yasaklar. CSP tek başına yeterli değildir, doğru kodlamanın yerini tutmaz; ama son bir güvenlik ağı olarak değerlidir. Politikayı önce yalnız rapor modunda çalıştırıp neyi kıracağını görmek iyi bir alışkanlıktır.
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'
Etkinlik 5 / 6
Yorum alanından gelen metni HTML sayfasına basacaksın. Hangi önlemler doğrudan işe yarar?
Öğrendiğini yeni bir bağlama taşı. Bir alışveriş sitesinde zararlı bir yorum bir kez yazılıyor, veritabanına düşüyor ve sonrasında ürünü açan her ziyaretçide kendiliğinden çalışıyor. Yükün kalıcı olması ve her ziyaretçiyi etkilemesi hangi XSS tipini işaret eder? Cevabı bulduktan sonra bir de savunmayı düşün.
Asıl kararı sayfaya basarken ver: içeriği çıktı bağlamına göre kodla. Böylece depoda ne durursa dursun, tarayıcıya kod olarak gitmez. Kayıt anındaki arındırma da ikinci bir katman olarak eklenebilir.
Etkinlik 6 / 6
Saklı XSS'te zararlı metin ____ kaydedilir ve her ziyaretçide çalışır.
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.