24.12.2025

JavaScript’te Güvenli DOM Manipülasyonu: XSS’e Davetiye Çıkarmadan HTML Üretmek

innerHTML tuzaklarından kaçınarak DOM’u güvenli biçimde güncelleme: sanitize, template, Trusted Types ve örnekler.

Modern web uygulamalarında DOM’a veri basmak kaçınılmaz. Asıl mesele, bunu XSS (Cross-Site Scripting) riskini büyütmeden yapmak. Bu yazıda performans veya framework kıyasına girmeden, doğrudan güvenli DOM manipülasyonu pratiklerini ele alacağız.

1) innerHTML ne zaman problem olur?

Kullanıcıdan (veya dış kaynaktan) gelen bir veriyi innerHTML ile basmak, basit bir hatayla script çalıştırmaya dönüşebilir.

// KÖTÜ: Dış kaynaktan gelen veri HTML olarak işlenir
const comment = getCommentFromApi();
document.querySelector('#box').innerHTML = comment;

Bir saldırganın hedefi genelde şudur: HTML içine event handler, script veya kötü niyetli URL enjekte etmek.

2) Varsayılan güvenli yol: textContent + DOM API

Metin basıyorsanız en güvenlisi: textContent.

const comment = getCommentFromApi();
document.querySelector('#box').textContent = comment;

HTML üretmeniz gerekiyorsa, string birleştirmek yerine element oluşturun:

const li = document.createElement('li');
li.textContent = item.title; // güvenli
list.appendChild(li);

Bu yaklaşım “sıkıcı” görünür ama saldırı yüzeyini ciddi biçimde küçültür.

3) HTML şartsa: sanitize edin (DOMPurify örneği)

Bazen rich-text (kalın, link, liste) gerekir. Bu durumda sanitize şarttır. Yaygın çözüm: DOMPurify.

import DOMPurify from 'dompurify';

const raw = getRichText();
const clean = DOMPurify.sanitize(raw, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'p', 'br'],
  ALLOWED_ATTR: ['href', 'title', 'target', 'rel']
});

document.querySelector('#content').innerHTML = clean;

Ek ipucu: Link’lerde rel="noopener noreferrer" kullanmayı zorunlu kılın.

4) Template üretirken “güvenli varsayılanlar”

Şablon stringleriyle HTML üretmek pratik ama riskli:

// RİSKLİ
card.innerHTML = `<h3>${title}</h3>`;

Bunun yerine ya tamamen DOM API kullanın ya da sadece güvenli alanlarda template kullanın. Örneğin başlık metinse:

const h3 = document.createElement('h3');
h3.textContent = title;
card.append(h3);

5) “Görünmez” tehlike: URL ve attribute enjeksiyonu

Sadece HTML değil, attribute set etmek de riskli olabilir. Özellikle href, src, style.

Kural: Dış kaynaktan gelen URL’leri whitelist ile doğrulayın.

function safeUrl(input) {
  const url = new URL(input, location.origin);
  const allowed = ['https:', 'http:'];
  if (!allowed.includes(url.protocol)) throw new Error('Blocked protocol');
  return url.toString();
}

const a = document.createElement('a');
a.textContent = 'Profil';
a.href = safeUrl(profileUrlFromApi);

javascript: gibi protokoller bu şekilde engellenir.

6) CSP ve Trusted Types: “Yanlış yapmayı” zorlaştırın

Kod disiplinine ek olarak tarayıcı seviyesinde kalkan koyabilirsiniz:

  • Content Security Policy (CSP): Inline script’leri ve izinsiz kaynakları engeller.
  • Trusted Types: innerHTML gibi sink’lere rastgele string basılmasını zorlaştırır.

Örnek CSP (özet):

Content-Security-Policy: script-src 'self'; object-src 'none'; base-uri 'self';

Trusted Types tarafı proje yapılandırmasına göre değişir; fikir şu: sanitize edilmemiş string’in DOM’a girmesini “derleme zamanı” gibi erken yakalamak.

7) Mini kontrol listesi

  • Metin bas: textContent
  • Element üret: createElement + append
  • Rich-text gerekiyorsa: sanitize (DOMPurify vb.)
  • URL/attribute set ederken: doğrula/whitelist
  • CSP (mümkünse) + Trusted Types (özellikle büyük projelerde)

Güvenli DOM manipülasyonu, en iyi ihtimalle “fazladan birkaç satır kod”, en kötü ihtimalle ise bir veri sızıntısını engelleyen farktır.