October 13, 2026
Bir Hacker Gibi Düşünmek — #10
XSS Nedir? Bir Web Sayfasına Zararlı JavaScript Nasıl Enjekte Edilir?

By Abdulkadir Kayacan
8 min read
XSS Nedir? Bir Web Sayfasına Zararlı JavaScript Nasıl Enjekte Edilir?
Bir web sitesine girdiğini ve yorumlar bölümüne bir mesaj yazdığını düşün. Mesajını gönderiyorsun, sayfa yenileniyor ve yorumun diğer kullanıcılarla birlikte görüntüleniyor. Normal şartlarda bu oldukça sıradan bir işlem. Ancak web sitesi, gönderdiğin metni doğru şekilde işlemiyorsa işler değişebilir.
Peki, yazdığın bir yorum yalnızca ekranda görüntülenmesi gereken metin olmaktan çıkıp tarayıcıda JavaScript kodu olarak çalışırsa ne olur? Üstelik bu kodu yalnızca sen değil, aynı sayfayı ziyaret eden diğer kullanıcılar da çalıştırıyorsa?
İşte bu senaryo, web güvenliğinin en önemli konularından biri olan Cross-Site Scripting (XSS) açığının arkasındaki temel mantığı anlatıyor.
XSS, bir web uygulamasının güvenilmeyen girdileri güvenli şekilde işlememesi sonucunda saldırgan tarafından sağlanan kodun başka bir kullanıcının tarayıcısında çalışmasına neden olabilen bir güvenlik açığıdır. Bu durum, uygulamanın yapısına ve açığın türüne bağlı olarak kullanıcı adına işlem yapılmasına, hassas bilgilerin açığa çıkmasına veya sayfanın içeriğinin değiştirilmesine yol açabilir.
Bu yazıda XSS'in ne olduğunu, tarayıcıda JavaScript'in nasıl çalıştığını, XSS türlerini ve bu açıklara karşı nasıl korunabileceğimizi inceleyeceğiz. Amacımız yalnızca bir saldırı tekniğini öğrenmek değil, web uygulamalarının kullanıcı girdilerini neden dikkatli işlemesi gerektiğini anlamak.
Önce Tarayıcı ve JavaScript Nasıl Çalışıyor?
XSS'i anlamak için önce web sayfalarının tarayıcıda nasıl oluşturulduğunu bilmemiz gerekiyor.
Bir web sayfası genellikle HTML, CSS ve JavaScript gibi teknolojilerden yararlanır. HTML sayfanın yapısını oluşturur, CSS görünümünü düzenler, JavaScript ise sayfaya etkileşim ve dinamik davranış kazandırır.
Örneğin, aşağıdaki HTML kodunu inceleyelim:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Web Sayfası</title>
</head>
<body>
<h1>Merhaba Kadir</h1>
<button onclick="alert('Butona tıklandı!')">
Tıkla
</button>
</body>
</html><!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Web Sayfası</title>
</head>
<body>
<h1>Merhaba Kadir</h1>
<button onclick="alert('Butona tıklandı!')">
Tıkla
</button>
</body>
</html>Bu sayfayı tarayıcıda açtığında ekranda bir başlık ve bir buton görürsün. Butona tıkladığında JavaScript çalışır ve bir uyarı penceresi açılır.
Burada önemli olan, tarayıcının HTML ve JavaScript kodlarını yorumlayarak sayfayı oluşturmasıdır. Tarayıcı yalnızca metin görüntülemez; kendisine verilen içeriğin hangi bölümünün HTML, hangi bölümünün JavaScript olduğunu da değerlendirir.
Bu özellik web uygulamalarının çalışması için gereklidir. Ancak uygulama, kullanıcıdan aldığı metni yanlış şekilde HTML veya JavaScript olarak yorumlanabilecek bir bağlama yerleştirirse güvenlik sorunu ortaya çıkabilir.
XSS'in temelinde de bu ayrım vardır: Uygulamanın yalnızca veri olarak göstermesi gereken içerik, tarayıcı tarafından çalıştırılabilir kod olarak yorumlanır.
XSS Açığı Nasıl Ortaya Çıkar?
Bir web sitesinin kullanıcıların mesaj bırakabildiği bir yorum alanı olduğunu düşünelim. Kullanıcı bir yorum gönderdiğinde uygulama bu yorumu veritabanında saklıyor ve daha sonra sayfada gösteriyor.
Güvenli bir uygulama, yorumu normal metin olarak görüntülemelidir. Ancak uygulama, kullanıcıdan gelen değeri kontrolsüz biçimde HTML içine yerleştirirse tarayıcı bu içeriğin bir bölümünü kod olarak yorumlayabilir.
Örneğin, şu kod parçasını ele alalım:
<div id="comment"></div>
<script>
const comment = "<img src=x onerror=alert('XSS')>";
document.getElementById("comment").innerHTML = comment;
</script><div id="comment"></div>
<script>
const comment = "<img src=x onerror=alert('XSS')>";
document.getElementById("comment").innerHTML = comment;
</script>Bu örnek yalnızca yerel bir test sayfasında incelenmek üzere hazırlanmıştır. Burada innerHTML, verilen metni sıradan yazı olarak göstermek yerine HTML olarak ayrıştırır. İçeriğin içindeki görsel yüklenemediğinde onerror olay işleyicisi devreye girebilir ve uyarı penceresi açılabilir.
Bu küçük örnek, güvenli olmayan HTML yerleştirmenin nasıl kod çalıştırılmasına yol açabileceğini gösterir. Gerçek bir uygulamada saldırının mümkün olup olmadığı, kullanılan bağlama, tarayıcı davranışına ve uygulamanın güvenlik önlemlerine bağlıdır.
Aynı içeriği textContent ile ekleseydik durum farklı olurdu:
const comment = "<img src=x onerror=alert('XSS')>";
document.getElementById("comment").textContent = comment;const comment = "<img src=x onerror=alert('XSS')>";
document.getElementById("comment").textContent = comment;Bu durumda tarayıcı, içeriği HTML olarak ayrıştırmak yerine metin olarak gösterir. Kullanıcı ekranda <img ...> ifadesini görür, ancak bu ifade HTML öğesi olarak oluşturulmaz.
Buradan çıkaracağımız ilk ders şudur: Kullanıcıdan gelen veriyi görüntülerken verinin hangi bağlama yerleştirildiği büyük önem taşır. Metni metin olarak eklemek ile onu HTML olarak yorumlatmak aynı şey değildir.
XSS Neden Tehlikelidir?
Bir uyarı penceresi açmak, XSS öğrenirken kullanılan klasik bir örnektir. Ancak gerçek güvenlik sorunlarının boyutunu yalnızca bu örnekle değerlendirmek yanıltıcı olur.
Asıl tehlike, saldırganın sağladığı JavaScript kodunun sayfayı ziyaret eden kullanıcının tarayıcısında, o sayfanın güvenlik bağlamı içinde çalışabilmesidir.
Örneğin, bir kullanıcının oturum açtığı web sitesinde XSS açığı bulunuyorsa kötü amaçlı kod, kullanıcının tarayıcısı üzerinden uygulamanın sunduğu bazı işlemleri gerçekleştirmeye çalışabilir. Bu işlemler, kullanıcının yetkilerine ve uygulamanın korumalarına bağlıdır.
Önceki yazımızda cookie ve session kavramlarını öğrenmiştik. Burada önemli bir bağlantı var: XSS, bazı durumlarda oturum bilgilerinin kötüye kullanılmasına yol açabilir. Ancak HttpOnly niteliğiyle korunan bir cookie, normal JavaScript API'leri üzerinden okunamaz. Bu, oturum cookie'sinin doğrudan çalınması riskini azaltır.
Yine de HttpOnly, XSS'i tamamen etkisiz hâle getirmez. Kötü amaçlı bir betik, cookie değerini okuyamasa bile kullanıcının tarayıcısı üzerinden bazı isteklere neden olabilir. Bu nedenle XSS'e karşı koruma, yalnızca cookie ayarlarına bırakılmamalıdır.
XSS'in olası sonuçları arasında sayfa içeriğinin değiştirilmesi, kullanıcıya sahte giriş formları gösterilmesi, hassas bilgilerin açığa çıkarılması ve kullanıcının yetkileri kapsamında bazı işlemlerin gerçekleştirilmesi bulunabilir. Her XSS açığı aynı etkiye sahip değildir; risk, açığın nerede bulunduğuna ve uygulamanın nasıl tasarlandığına göre değişir.
XSS Türleri
XSS tek bir yöntemden ibaret değildir. Güvenlik araştırmalarında genellikle üç temel türden söz edilir: Reflected XSS, Stored XSS ve DOM-based XSS.
1. Reflected XSS — Yansıtılan XSS
Reflected XSS'te saldırganın sağladığı girdi, sunucudan dönen yanıt içinde güvenli olmayan biçimde yer alır. Girdi çoğunlukla bir URL parametresi veya form alanı üzerinden sunucuya ulaşır ve uygulama bunu yanıtın içine doğru şekilde kodlamadan ekler.
Örneğin, bir arama sayfasının şu adresle çalıştığını düşünelim:
https://example.com/search?q=linuxhttps://example.com/search?q=linuxUygulama, arama terimini sayfada şöyle gösteriyor olabilir:
"linux" için arama sonuçları"linux" için arama sonuçlarıBu davranış normaldir. Ancak uygulama, q parametresindeki değeri HTML içine kontrolsüz şekilde yerleştiriyorsa özel olarak hazırlanmış bir girdi, yanıtın yapısını değiştirebilir.
Reflected XSS'in önemli bir özelliği, içeriğin genellikle kalıcı olarak saklanmamasıdır. Kullanıcı girdisi isteğe bağlı olarak gelir ve aynı isteğe verilen yanıtta yansıtılır. Bu tür açıklar bazen kurbanın özel hazırlanmış bir bağlantıyı açmasına dayanır.
Ancak her URL parametresinin sayfada görünmesi güvenlik açığı olduğu anlamına gelmez. Önemli olan, verinin yanıt içinde nasıl işlendiği ve tarayıcı tarafından çalıştırılabilir içerik olarak yorumlanıp yorumlanmadığıdır.
2. Stored XSS — Saklanan XSS
Stored XSS, yani saklanan XSS, saldırganın sağladığı içeriğin uygulamada saklanması ve daha sonra başka kullanıcıların tarayıcılarında güvenli olmayan biçimde görüntülenmesiyle ortaya çıkar.
Bir forumun yorum sistemini düşünelim. Kullanıcı yorumunu gönderiyor, uygulama bunu veritabanına kaydediyor ve sayfayı ziyaret eden herkes yorumları görebiliyor.
Eğer uygulama zararlı içeriği güvenli şekilde işlemiyorsa aynı içerik, yorumu görüntüleyen her kullanıcı için çalışabilir. Bu nedenle Stored XSS, özellikle çok sayıda kişinin kullandığı uygulamalarda ciddi bir risk oluşturabilir.
Bu tür açıklar yorumlar, profil açıklamaları, mesajlar, ürün incelemeleri veya kullanıcıların içerik oluşturabildiği diğer alanlarda görülebilir.
Reflected XSS ile Stored XSS arasındaki temel fark, girdinin nasıl geri döndüğüdür. Reflected XSS'te girdi çoğunlukla aynı isteğin yanıtında yansıtılırken Stored XSS'te içerik saklanır ve daha sonraki isteklerde tekrar sunulur.
3. DOM-based XSS — DOM Tabanlı XSS
DOM-based XSS, tarayıcı tarafında çalışan JavaScript kodunun güvenilmeyen verileri tehlikeli biçimde işlemesi sonucunda ortaya çıkabilir.
DOM, yani Document Object Model, tarayıcının HTML belgesini programatik olarak temsil etmesini sağlar. JavaScript, DOM aracılığıyla sayfadaki öğeleri okuyabilir ve değiştirebilir.
Örneğin, aşağıdaki kodu inceleyelim:
const params = new URLSearchParams(window.location.search);
const name = params.get("name");
document.getElementById("welcome").innerHTML = "Merhaba, " + name;const params = new URLSearchParams(window.location.search);
const name = params.get("name");
document.getElementById("welcome").innerHTML = "Merhaba, " + name;Bu kod, URL'deki name parametresini alıyor ve innerHTML kullanarak sayfaya ekliyor. Ancak parametrenin güvenli bir metin olduğu varsayılıyor. Kullanıcı girdisi HTML olarak yorumlanabilecek içerik taşıyorsa güvenlik riski doğabilir.
Daha güvenli yaklaşım, burada innerHTML yerine textContent kullanmaktır:
const params = new URLSearchParams(window.location.search);
const name = params.get("name");
document.getElementById("welcome").textContent =
"Merhaba, " + name;const params = new URLSearchParams(window.location.search);
const name = params.get("name");
document.getElementById("welcome").textContent =
"Merhaba, " + name;Bu sürüm, kullanıcıdan alınan değeri metin olarak ekler. Böylece girdinin HTML olarak yorumlanması önlenir.
DOM-based XSS'in önemli bir özelliği, sorunun doğrudan tarayıcıda çalışan kodda bulunabilmesidir. Sunucu girdiyi yanıt içinde güvenli biçimde döndürse bile istemci tarafındaki JavaScript bu girdiyi daha sonra tehlikeli bir bağlamda kullanabilir.
XSS ile SQL Injection Arasındaki Fark
Bir önceki yazımızda SQL Injection konusunu ele almıştık. İki açık türü de güvenilmeyen girdilerin yanlış işlenmesiyle ilişkilidir. Ancak etkilenen bileşen ve ortaya çıkan sonuç farklıdır.
SQL Injection'da temel problem, kullanıcı girdisinin SQL sorgusunun yapısını değiştirebilmesidir. Risk çoğunlukla veritabanı işlemleriyle ilgilidir.
XSS'te ise temel problem, kullanıcıdan gelen içeriğin başka bir kullanıcının tarayıcısında çalıştırılabilir kod olarak yorumlanabilmesidir. Bu nedenle etki genellikle web sayfası ve kullanıcı oturumu bağlamında ortaya çıkar.
Kısaca SQL Injection, veritabanına gönderilen sorguların güvenli oluşturulmasıyla; XSS ise tarayıcıya sunulan içeriğin güvenli işlenmesiyle yakından ilişkilidir. Her iki durumda da kullanıcı girdisini kontrolsüz biçimde kod veya komut bağlamına yerleştirmek önemli bir risk oluşturur.
XSS'ten Nasıl Korunulur?
XSS'i anlamanın en önemli parçalarından biri, geliştiricilerin bu açığı nasıl önleyebileceğini bilmektir. Tek bir güvenlik önlemi her durumda yeterli olmayabilir; etkili koruma, uygulamanın veriyi nasıl aldığına, sakladığına ve görüntülediğine göre tasarlanmalıdır.
Kullanıcı girdisini metin olarak işle
En temel yaklaşım, kullanıcıdan gelen içeriği gereksiz yere HTML olarak yorumlatmamaktır. JavaScript'te sıradan metin eklemek için textContent kullanmak, innerHTML kullanmaktan genellikle daha güvenlidir.
Örneğin, kullanıcı adı gibi bir değeri sayfaya eklerken şu yaklaşım tercih edilebilir:
const username = userInput;
document.getElementById("username").textContent = username;const username = userInput;
document.getElementById("username").textContent = username;Burada değer, HTML kodu olarak ayrıştırılmaz. Bu, özellikle kullanıcı tarafından kontrol edilen basit metinler için doğru bir varsayılan yaklaşımdır.
Bağlama uygun çıktı kodlama kullan
Bazen uygulamanın kullanıcı girdisini HTML içinde göstermesi gerekir. Bu durumda verinin hangi bağlamda kullanıldığı önemlidir. HTML metni, HTML niteliği, JavaScript kodu ve URL bağlamları aynı kurallara sahip değildir.
Bu nedenle genel amaçlı, tek bir karakter değiştirme işleminin her yerde güvenli olacağını düşünmek yanlıştır. Kullanılan bağlama uygun çıktı kodlama yöntemleri tercih edilmeli ve mümkün olduğunda güvenli şablonlama sistemlerinden yararlanılmalıdır.
Örneğin, HTML metni içinde &, <, > ve uygun bağlamlarda tırnak işaretleri gibi karakterlerin doğru kodlanması, tarayıcının kullanıcı girdisini HTML yapısı olarak yorumlamasını önlemeye yardımcı olur. Ancak bu işlemin doğru uygulanması, verinin hangi bağlamda kullanıldığına bağlıdır.
HTML kabul ediyorsan temizleme uygula
Bazı uygulamalar kullanıcıların biçimlendirilmiş içerik göndermesine izin verir. Örneğin, bir blog platformu kalın yazı, bağlantı veya liste oluşturulmasını destekleyebilir.
Bu tür durumlarda her HTML etiketini tamamen reddetmek mümkün olmayabilir. Ancak kullanıcı içeriğini doğrudan HTML olarak kabul etmek de güvenli değildir.
Bunun yerine, izin verilen etiketleri ve nitelikleri açıkça belirleyen, bu iş için tasarlanmış bir HTML temizleme kütüphanesi kullanılmalıdır. Böylece desteklenmesi gereken biçimlendirme korunurken tehlikeli içeriklerin kaldırılması amaçlanır.
Elle birkaç karakteri silerek kapsamlı bir HTML temizleme mekanizması oluşturmak, karmaşık HTML ayrıştırma kuralları nedeniyle güvenilir bir çözüm değildir.
Content Security Policy (CSP) kullan
Content Security Policy, kısaca CSP, tarayıcının hangi kaynaklardan belirli içerikleri yükleyebileceğini ve hangi tür betiklerin çalıştırılabileceğini sınırlandırmaya yardımcı olan bir güvenlik mekanizmasıdır.
Bir web sunucusu, uygun bir CSP politikasını HTTP yanıt başlığıyla gönderebilir. Örneğin:
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'Bu örnek, kaynak yükleme davranışları için bazı kısıtlamalar getirir. Ancak tek başına bütün XSS türlerini otomatik olarak engelleyen eksiksiz bir politika değildir. Gerçek uygulamalarda betiklerin nasıl yüklendiği, hangi kaynakların gerekli olduğu ve güvenli bir CSP'nin nasıl oluşturulacağı dikkatlice değerlendirilmelidir.
CSP, XSS'e karşı ek bir savunma katmanı olarak düşünülmelidir; güvenli kodlama uygulamalarının yerini almamalıdır.
Cookie'leri güvenli yapılandır
Önceki yazımızda HttpOnly, Secure ve SameSite cookie niteliklerini öğrenmiştik. Bunlar oturum güvenliğini destekleyebilir.
Özellikle HttpOnly, JavaScript'in oturum cookie'sini doğrudan okumasını engellemeye yardımcı olur. Ancak XSS açığı varsa saldırganın sağladığı kod, kullanıcının tarayıcısı üzerinden uygulamaya istek göndermeyi deneyebilir. Bu nedenle cookie güvenliği ve XSS koruması birlikte ele alınmalıdır.
Kendi Bilgisayarında XSS'i Nasıl Öğrenebilirsin?
XSS'i uygulamalı öğrenmek için en iyi başlangıç, küçük bir HTML dosyası oluşturup tarayıcıda çalıştırmaktır. Yukarıdaki innerHTML ve textContent örneklerini aynı dosyada karşılaştırabilirsin.
Önce innerHTML kullanan sürümde içeriğin HTML olarak ayrıştırıldığını gözlemle. Ardından aynı kodu textContent ile değiştir ve girdinin artık metin olarak gösterildiğini kontrol et. Böylece yalnızca kavramı okumak yerine tarayıcının davranışını doğrudan gözlemlemiş olursun.
Bir sonraki aşamada, eğitim amacıyla hazırlanmış laboratuvarlardan yararlanabilirsin. PortSwigger Web Security Academy'nin XSS eğitimleri ve OWASP WebGoat, konuyu kontrollü ortamda öğrenmek için faydalı kaynaklardır.
Testlerini kendi oluşturduğun uygulamalarda veya açıkça izin verilmiş laboratuvarlarda yapmalısın. Başka kullanıcıların hesaplarında ya da gerçek web sitelerinde izinsiz XSS denemeleri yapmak, hem güvenlik hem de hukuki açıdan sorunlara yol açabilir.
Sonuç: Tarayıcı Her Metni Aynı Şekilde Yorumlamaz
XSS, bir web uygulamasının güvenilmeyen girdileri doğru şekilde işlememesi sonucunda kullanıcı tarafından sağlanan içeriğin başka bir kullanıcının tarayıcısında kod olarak çalışmasına neden olabilen önemli bir güvenlik açığıdır.
Reflected XSS, Stored XSS ve DOM-based XSS farklı yollarla ortaya çıkabilir. Ancak hepsinde temel soru benzerdir: Uygulamanın veri olarak kabul etmesi gereken bir içerik, tarayıcı tarafından çalıştırılabilir kod olarak yorumlanıyor mu?
Bir hacker gibi düşünmek, yalnızca bir sayfada JavaScript çalıştırmayı denemek değildir. Uygulamanın veriyi nereden aldığını, nasıl işlediğini ve hangi noktada tarayıcıya aktardığını anlamaktır. Güvenlik açısından asıl önemli olan, veri ile çalıştırılabilir kod arasındaki sınırın nerede korunması gerektiğini bilmektir.
Geliştiriciler açısından güvenli DOM API'lerini tercih etmek, bağlama uygun çıktı kodlama uygulamak, gerektiğinde güvenilir HTML temizleme kütüphaneleri kullanmak ve CSP gibi ek savunma mekanizmalarını doğru yapılandırmak XSS riskini azaltmaya yardımcı olur.
Bir sonraki yazıda serimize "Bir Hacker Gibi Düşünmek — #11: CSRF Nedir? Bir Kullanıcı Farkında Olmadan Nasıl İşlem Yaptırılabilir?" başlığıyla devam edeceğiz. Böylece tarayıcının kullanıcı adına istek göndermesi ile bir uygulamanın bu isteğin gerçekten kullanıcı tarafından amaçlanıp amaçlanmadığını nasıl değerlendirdiğini inceleyebiliriz.