DOM-based XSS: how client-side JavaScript creates injection sinks
DOM XSS sources, DOM XSS sinks, location.hash, document.write, eval risks, jQuery html(), trusted types, sanitize-html, DOMPurify
DOM-Based XSS
DOM XSS never touches the server. JavaScript on your own page reads from an attacker-controlled source and writes to a dangerous sink without sanitization. Traditional server-side defenses do not help because the payload stays client-side.
Common Sources
Attacker-controlled sources include: location.hash, location.search, document.referrer, postMessage data, and window.name.
Common Sinks
// DANGEROUS sinks — never pass unsanitized input to these
element.innerHTML = data; // Parses HTML and executes scripts
document.write(data); // Same as innerHTML
eval(data); // Direct code execution
setTimeout(data); // Executes string as code if passed as string
jQuery(element).html(data); // jQuery innerHTML wrapper
location.href = data; // URL-based XSS if data = javascript:...Safe Alternatives
// Use textContent instead of innerHTML for plain text
element.textContent = data;
// If you must render HTML (rich text editor), sanitize first
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(data);
// For URLs, validate the protocol
function safeUrl(url) {
const parsed = new URL(url);
if (!['http:', 'https:'].includes(parsed.protocol)) {
return '#';
}
return url;
}Trusted Types API
Chrome and Edge support the Trusted Types API, which makes dangerous sinks type-safe—they only accept values created through sanitization policies, preventing accidental unsafe assignments. Enable it with: Content-Security-Policy: require-trusted-types-for 'script'.
