DOM Tabanlı Cross Site Scripting (XSS) Zafiyeti
Bu yazımızda, Reflected ve Stored XSS kadar yoğun işlenmeyen ama zafiyetin doğuracağı tehlikeler açısından pek bir farkı bulunmayan, Google ve Yahoo gibi popüler ve bilinen firmaların web sitelerinde de tespit edilen DOM XSS zafiyetini inceleyeceğiz. Basit bir örnek üzerinden açıklamaya çalıştığımız DOM XSS zafiyeti için çözüm önerlerini de konuşacağız.
Cross Site Scripting (XSS), içinde bulunduğumuz dönemde geliştiriciler arasında oldukça iyi tanınan bir güvenlik açığıdır. Dolayısıyla XSS’in ne olduğunu tekrar anlatmaya gerek yok. Bir XSS saldırısının etkisine dair geliştiriciler tarafından iyi anlaşılması gereken en önemli kısım şu ki bir saldırgan oturum bilginizi (session) çalabilir ya da oturumunuzu ele geçirebilir. Yanı sıra başarılı phishing saldırıları düzenleyebilir. Kısacası kurbanın yapabildiği her şeyi etkin bir şekilde yapma becerisine sahip olur.
Basitçe tanımlamak gerekirse “DOM Tabanlı XSS, bir HTML parçasında değil DOM (Document Object Model)’da meydana gelen XSS zafiyetidir” denilebilir. Stored ve Reflected XSS saldırılarında, saldırı sonrası dönen sayfada XSS atağını görmek mümkünken; DOM tabanlı XSS saldırılarında HTML kaynağı ve dönen yanıt, tamamen aynı olacaktır. Başka bir deyişle, XSS atağı dönen sayfada bulunamaz ve sadece runtime sırasında ya da sayfanın DOM’u incelenirken gözlemlenebilir.
Basit Bir DOM Tabanlı Cross Site Scripting Zafiyeti Örneği
Aşağıdaki kodları içeren şu sayfayı düşünelim http://www.example.com/test.html:
<script>
document.write("<b>Geçerli URL</b> : " + document.baseURI);
</script>
http://www.example.com/test.html#<script>alert(1)</script> gibi bir HTTP isteği gönderdiğinizde JavaScript kodunun çalışması için yeterli olacaktır. Çünkü siz document.write fonksiyonuyla URL’ye ne yazarsanız sayfa da onu ekrana yazar. Sayfanın kaynağına baktığınızdaysa <script>alert(1)</script> kodunu görmezsiniz. Bunun sebebiyse olan biten her şeyin DOM’da gerçekleşmesi ve JavaScript kodu tarafından çalıştırılmasıdır.
Zararlı JavaScript kodu sayfa tarafından çalıştırıldıktan sonra, kullanıcıya ait çerezlerin (cookie) çalınması veya sayfa davranışının istenildiği gibi değiştirilmesi amacıyla, DOM tabanlı XSS zafiyetinin basit bir şekilde exploit edilmesi mümkündür.
DOM XSS Zafiyeti Gerçek Bir Tehdittir
Çeşitli araştırma ve çalışmalar göstermiştir ki web sitelerinin %50’ye yakını DOM tabanlı XSS açığına karşı savunmasızdır. Google, Yahoo ve Alexa gibi popüler ve bilinen firmaların sitelerinde DOM Tabanlı XSS zafiyetleri tespit edilmiştir.
Sunucu Taraflı Filtrelerin Hiçbir Önemi Yok
Reflected ve Stored XSS zafiyetleriyle DOM Tabanlı XSS zafiyeti arasındaki en büyük fark, DOM Tabanlı XSS’nin server-side (sunucu taraflı) filtrelerle durdurulamamasıdır. Bunun çok basit bir sebebi bulunmakta: ‘#’ (hash) karakterinden sonra yazılan hiçbir şey sunucuya gönderilmez, yani HTTP trafiğinde gözükmez.
Hash olarak da bilinen bölüm geçmişte, HTML sayfayı belli bir elemente doğru kaydırma amacıyla tanıtılmıştı. Fakat ilerleyen dönemlerde JavaScript geliştiricileri tarafından AJAX kullanılan sayfalarda, sayfa kayıtlarını ve buna benzer şeyleri tutmak için çoğunlukla hash-bang “#!” olarak kullanılmıştır.
Bu tasarımdan dolayı hash’tan sonraki hiçbir şey sunucuya gönderilmeyecektir. Bu da göstermektedir ki DOM Tabanlı XSS zafiyetlerinde, kodda uygulanan sunucu taraflı hiçbir güvenlik yöntemi işe yaramayacaktır. İşin doğrusunu söylemek gerekirse, web uygulama güvenlik duvarlarına (WAF) benzer diğer korunma yöntemleri ya da ASP.NET Request Validation gibi kapsamlı framework önlemi, DOM Tabanlı XSS saldırılarına karşı koruma sağlamayacaktır.
Kaynak & Alıcı olarak isimlendirilen Girdi & Çıktı
DOM XSS’in ardındaki mantık şudur: Kullanıcıdan (source, kaynak) alınan girdi, bir yürütme noktasına (sink, alıcı) gider. Önceki örnekte kaynak document.baseURI iken, alıcıysa document.write idi.
Yine de DOM XSS’in, kullanıcı tarafından kontrol edilebilen bir kaynağın tehlikeli bir alıcıda kullanılmasıyla gerçekleştiğini bilmek gerekir.
Dolayısıyla, DOM XSS’e karşı savunmasız kalmamak için ihtiyaç duyulan kod değişikliklerinin yapılması ya da uygun kodlama (encoding) işleminin gerçekleştirilmesi gerekir.
Aşağıda, DOM XSS saldırılarında sıklıkla hedeflenen kaynak ve alıcıların bir listesi mevcut. Bu, tamamlanmış bir liste olmasa da nelerin sorun oluşturabileği konusunda size iyi bir fikir verecek. Eğer girdi/kaynak kullanıcı tarafından kontrol edilebiliyorsa ve çıktı/alıcı kısmında JavaScript kodu çalıştırılmasına yol açabilecekse, DOM XSS güvenlik açığı oluşabilir demektir.
Popüler Kaynaklar
- document.URL
- document.documentURI
- location.href
- location.search
- location.*
- window.name
- document.referrer
Popüler Alıcılar
- HTML modifikasyon alıcıları
- document.write
- (element).innerHTML
- Davranış değişimi için HTML modifikasyon
- (element).src (belli elementlerde)
- Yürütmeyle ilgili alıcılar
- eval
- setTimeout / setInterval
- execScript
DOM XSS'den Korunmak
DOM tabanlı XSS zafiyetlerinden en iyi korunma yolu doğru çıktı (sink) metodunu kullanmaktan geçer. Örneğin, bir kullanıcı girdisini <div> elementi içerisinde yazdırmak isterseniz, innerHTML’i kullanmayın. Bunun yerine DOM XSS zafiyetlerine karşı kullanılması daha doğru olan innerText / textContent metotlarını kullanın.
Eval gibi tehlikeli kaynaklarda da (source) kullanıcı tarafından kontrol edilen bir girdiyi kullanmak her zaman kötü bir fikir olacaktır. Zararlı olabilecek karakterleri temizlemek doğru metodu kullanmaya göre çok daha zor ve riskli bir çözüm olur.
Son olarak, ilk kodumuzdaki problemin çözümü için güçlük çıkarabilecek ve kolayca hataya neden olabilecek encode etme yöntemi yerine, çıktıyı içeriğe yazdırma amacıyla element.textContent metodunu şu şekilde kullanmamız yeterlidir:
<b>Geçerli URL: </b> <span id=“contentholder”></span>
<script>
document.getElementById(“contentholder”).textContent = document.baseURI;
</script>
Bu kod parçası da aynı işlemi yapmasına rağmen diğer kodun aksine, DOM tabanlı XSS zafiyetlerine karşı güvenlidir.