DOM-based XSS
DOM-based XSS happens entirely in the browser. Server response is benign; client JS reads attacker-controlled data (location, hash, postMessage) and writes it into the DOM unsafely.
Spot it + fix it
EXAMPLE
// VULNERABLE — reads location.hash and renders it as HTML
const route = location.hash.slice(1); // e.g. #<img onerror=alert(1)>
document.getElementById('box').innerHTML = '<h1>' + route + '</h1>';
// SAFE — create text nodes / use textContent
const h = document.createElement('h1');
h.textContent = route;
box.replaceChildren(h);
// SAFE — use the Sanitizer API where available
box.innerHTML = '';
box.setHTML(`<h1>${route}</h1>`); // strips dangerous markup
// Common dangerous sinks to grep for:
// innerHTML, outerHTML, insertAdjacentHTML, document.write,
// eval, Function, setTimeout(string, …), setInterval(string, …),
// href = 'javascript:…', src = 'javascript:…'
// Common dangerous sources:
// location.*, document.URL, document.referrer,
// window.name, postMessage data, fetch JSON without schema-validation
Why it matters
DOM XSS doesn’t touch the server, so server-side filtering doesn’t catch it. CSP + Trusted Types is your safety net — declare which strings can become script.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// VULNERABLE — reads window.location.hash and renders as HTML
const route = location.hash.slice(1);
document.title = route; // safe (text)
box.innerHTML = '<h1>' + route + '</h1>'; // UNSAFE — XSS
// SAFE
const h = document.createElement('h1');
h.textContent = route;
box.replaceChildren(h);
Try it Yourself »
Discussion
Loading…