Genel Bakış
DOM Tabanlı Cross-Site Scripting (DOM XSS), güvenlik açığının sunucu tarafından üretilen HTML'de değil, tamamen istemci taraflı JavaScript kodunda bulunduğu bir XSS türüdür. Saldırı yükü hiçbir zaman sunucuya ulaşmaz; bunun yerine tarayıcının kendi JavaScript'i, saldırgan kontrolündeki verileri bir kaynaktan okur ve yeterli temizleme yapılmadan, betik çalıştırılmasına neden olan tehlikeli bir havuza iletir. Sunucu yükü hiçbir zaman görmediğinden, sunucu taraflı girdi doğrulaması ve çıktı kodlaması tek başına saldırıyı önleyemez.
DOM XSS, CWE-79 kapsamında yansıtılan ve depolanan XSS ile birlikte kataloglanmış olsa da tamamen istemci taraflı veri akışı, hem tespit hem de düzeltme stratejileri açısından onu farklı kılar. Sensagraph, bilinen kaynaklar ile tehlikeli havuzlar arasındaki istemci taraflı JavaScript veri akışlarını analiz ederek DOM XSS'i tespit eder.
Nasıl çalışır?
Saldırı zinciri iki bileşenden oluşur: saldırgan kontrolündeki verinin JavaScript'e girdiği kaynak ve temizlenmemiş veri iletildiğinde kod çalıştırılmasına veya HTML enjeksiyonuna neden olan DOM API'si ya da özelliği olan havuz.
Yaygın Kaynaklar
location.hash— URL parçası, sunucuya hiç iletilmezlocation.search— sorgu dizesi parametrelerilocation.href,location.pathnamedocument.referrerwindow.namepostMessageolay verisi- Ele geçirilmiş bir kaynak tarafından yazılan
localStorage/sessionStoragedeğerleri - Saldırgan etkisindeki veri içeren WebSocket mesajları, XMLHttpRequest / Fetch yanıtları
Yaygın Tehlikeli Havuzlar
element.innerHTML,element.outerHTMLdocument.write(),document.writeln()eval(),setTimeout(string),setInterval(string),new Function(string)element.src,element.href(javascript:URI'larına ayarlandığında)location.href,location.assign(),location.replace()(açık yönlendirmeden XSS'e yükseltme)- jQuery yöntemleri: HTML dizeli
$(),.html(),.append()
Saldırı Örneği
Aşağıdaki savunmasız JavaScript parçacığını ele alalım:
// Savunmasız kod
const name = decodeURIComponent(location.hash.slice(1));
document.getElementById('greeting').innerHTML = 'Merhaba, ' + name;
Saldırgan, https://example.com/page#<img src=x onerror=alert(document.cookie)> URL'sini hazırlayıp kurbana iletir. Kurbanın tarayıcısı sayfayı yüklediğinde, parça değeri doğrudan innerHTML'e okunur ve enjekte edilen betik, example.com bağlamında çalıştırılır — yük hiçbir zaman sunucu isteğinde görünmez.
Neden Sunucu Taraflı Savunmalar Yetersiz Kalır?
- URL parçası (
#...), HTTP spesifikasyonu (RFC 3986 §3.5) gereği sunucuya iletilmez. - Sunucu taraflı temizleme ve WAF'lar, hiçbir zaman almadıkları HTTP istek verileri üzerinde çalışır.
- İçerik Güvenlik Politikası (CSP) etkiyi sınırlayabilir; ancak
unsafe-evalveyaunsafe-inlinemevcut olduğunda ya da havuz satır içi betik çalıştırılmasını tetiklemediğinde DOM XSS'i tam olarak önleyemez.
İş etkisi
Başarılı bir DOM XSS saldırısı, saldırgana hedef kaynak altında kurbanın tarayıcısında rastgele JavaScript çalıştırma imkânı verir. Olası sonuçlar şunlardır:
- Oturum ele geçirme —
HttpOnlybayrağı olmayan oturum çerezlerinin veya JavaScript'e erişilebilir depolarda tutulan token tabanlı kimlik bilgilerinin çalınması. - Kimlik bilgisi toplama — güvenilir sayfalara sahte giriş formları veya tuş kaydediciler enjekte edilmesi.
- Hesap devralma — kurban adına kimlik doğrulamalı işlemler gerçekleştirilmesi (betik aracılığıyla CSRF eşdeğeri).
- Veri sızdırma — hassas sayfa içeriğinin, DOM durumunun veya erişilebilir API'lerin okunması ve saldırgan kontrolündeki bir uç noktaya gönderilmesi.
- Kötü amaçlı yazılım dağıtımı — kurbanların istismar kitlerine yönlendirilmesi veya sürücüden indirme işlemlerinin başlatılması.
- Mevzuat riski — XSS kaynaklı ihlaller GDPR, PCI DSS veya HIPAA bildirim yükümlülüklerini ve cezalarını tetikleyebilir.
DOM XSS sıklıkla URL parçasını hedef aldığından ve sunucu taraflı günlüklerde iz bırakmadığından, adli inceleme ve olay müdahalesi sunucu taraflı XSS türlerine kıyasla daha karmaşıktır.
Nasıl düzeltilir?
- Mümkün olan her yerde tehlikeli havuzlardan kaçının. HTML ayrıştırmayan DOM API'lerini tercih edin: düz metin eklerken
element.innerHTMLyerineelement.textContentkullanın. DOM ağaçlarını programatik olarak oluşturmak içindocument.createElement()veappendChild()kullanın. - HTML havuzlarına yazmadan önce tüm verileri temizleyin. HTML çıktısı zorunluysa DOMPurify gibi güvenilir, aktif olarak desteklenen bir istemci taraflı temizleme kütüphanesi kullanın. Her zaman kısıtlayıcı biçimde yapılandırın (örn.
javascript:URI'larını yasaklayın).// DOMPurify'ın güvenli kullanımı const name = decodeURIComponent(location.hash.slice(1)); document.getElementById('greeting').innerHTML = 'Merhaba, ' + DOMPurify.sanitize(name); - Bağlama duyarlı çıktı kodlaması uygulayın. Verinin ekleneceği spesifik bağlama göre kodlayın (HTML gövdesi, HTML niteliği, JavaScript dizesi, CSS, URL). OWASP Java Encoder (sunucu taraflı) veya DOMPurify / he.js (istemci taraflı) gibi kütüphaneler bağlama duyarlı kodlama sağlar.
- Saldırgan kontrolündeki dizeleri JavaScript çalıştırma havuzlarına iletmekten kaçının. Kullanıcı tarafından sağlanan verileri hiçbir zaman
eval(), string bağımsız değişkeniylesetTimeout/setInterval,new Function()veyadocument.write()'a iletmeyin. - Katı bir İçerik Güvenlik Politikası (CSP) uygulayın.
script-src 'nonce-{rastgele}'veyascript-src 'strict-dynamic'içeren bir CSP, DOM XSS açığı mevcut olsa bile satır içi betik tabanlı istismarların büyük bölümünü engeller.unsafe-inlineveunsafe-evalyönergelerini kaldırın. postMessagekaynaklarını doğrulayın ve kısıtlayın.postMessageverilerini işlemeden önce her zamanevent.origin'i bir izin listesine göre doğrulayın.postMessageverilerini hiçbir zaman doğrudan tehlikeli bir havuza iletmeyin.- JavaScript çerçevelerini ve üçüncü taraf kütüphaneleri denetleyin. Birçok çerçeve (React, Angular, Vue) varsayılan olarak güvenli veri bağlama sağlar; ancak hepsi — React'ın
dangerouslySetInnerHTML'i, Angular'ınbypassSecurityTrust*'ı ve Vue'nunv-htmldirektifi — yanlış kullanıldığında bu korumaları atlayıp DOM XSS riskini yeniden ortaya çıkaran kaçış kapıları sunar. Tüm kullanımları denetleyin. - Kod incelemesi ve otomatik SAST/DAST taraması yapın. JavaScript veri akışlarında iz takibi yapabilen statik analiz araçlarını kullanarak kaynak-havuz yollarını belirleyin. Dinamik testler parça tabanlı ve
postMessagetabanlı yükleri kapsamalıdır. - Desteklendiği yerlerde Trusted Types benimseyin. Trusted Types tarayıcı API'si (
require-trusted-types-for 'script'CSP direktifiyle zorunlu kılınır), tehlikeli DOM havuzlarına yazılan değerlerin geliştirici tanımlı bir ilke tarafından üretilen bir Trusted Type nesnesinden geçmesini zorunlu kılar; bu da DOM XSS enjeksiyonunu yapısal olarak çok daha zorlaştırır.
Referanslar
- OWASP – DOM Based XSS
- OWASP Top 10 2021 – A03: Enjeksiyon
- OWASP – DOM Tabanlı XSS Önleme Kopya Kağıdı
- OWASP – XSS Önleme Kopya Kağıdı
- MITRE CWE-79 – Web Sayfası Oluşturma Sırasında Girişin Hatalı Nötrleştirilmesi
- PortSwigger Web Security Academy – DOM Tabanlı XSS
- MDN Web Docs – innerHTML Güvenlik Konuları
- W3C – Trusted Types Spesifikasyonu
- MDN Web Docs – Content-Security-Policy
- RFC 3986 §3.5 – URI Parça Tanımlayıcısı
- DOMPurify – Güvenilir HTML Temizleme Kütüphanesi