Login
ChallengesLearn
Scoreboard
Teams
Profile

Preferences

Truesapiens

LearnCross Site ScriptingDOM-based XSS
Cross Site Scripting·Lesson 4 of 12

DOM-based XSS

No server involved — the payload never leaves the browser. innerHTML, document.write, eval on a URL fragment — the client-side sink is the only boundary.

Intermediate12 min
XSSDOMClient-side
Loading lesson…
PreviousStored XSSNextXSS Defense

© 2026 Truesapiens.

Terms of ServicePrivacy PolicyCookie Policy

DOM-based XSS is a purely client-side variant of cross-site scripting. Unlike reflected or stored XSS, the server is never directly involved in the injection — the vulnerability exists entirely in the browser's own JavaScript code. The payload is read from a client-side source such as the URL fragment or window.nameand written to an HTML sink like innerHTML or document.write.

What you'll be able to do
  • Understand the difference between source-based and sink-based XSS.
  • Identify common DOM sinks: innerHTML, document.write, eval, setTimeout.
  • Recognise that DOM XSS never touches the server.
  • Apply safe DOM APIs and vet third-party JavaScript for sink usage.
Key terms
DOM XSS
A client-side XSS variant where the payload reaches a DOM sink through a client-side source (URL fragment, localStorage, window.name) without server involvement.
Source
A client-side data location that an attacker can control — typically location.hash, location.search, document.referrer, or window.name.
Sink
A DOM API that interprets the input as code or markup — innerHTML, outerHTML, document.write, eval, setTimeout with a string, and setAttribute with javascript: URLs.
Hash fragment
The part of a URL after the # symbol. The browser never sends the fragment to the server, making it invisible to server-side logging and WAFs.
What is it?

The server never sees it

DOM XSS exploits the separation between server-rendered HTML and client-side JavaScript. A page may be served safely from the server, but if its JavaScript reads from a browser-controlled source and writes to an HTML sink, an attacker can inject code that the server never detects. The URL hash fragment (#payload) is the most popular source because it is not sent to the server at all — server-side defences and web application firewalls cannot inspect it.

Common vulnerable patterns include single-page applications that read route parameters from the hash, page preview features that write a user-supplied name into the DOM, and search widgets that update the page content via innerHTML based on URL parameters. The attack surface grows with every third-party script and browser extension that has access to the DOM.

DOM XSS attack flow
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

Profile page with hash injection

Type a payload into the URL hash field below. The profile page reads the hash and writes it into the page using innerHTML. In vulnerable mode the browser executes whatever HTML you supply. Safe mode uses textContent instead, which treats the payload as plain text.

prodprofile.app/u/demo#
profile-app
profile-app · hash router

URL hash control

The profile page reads location.hash and writes it to the DOM.

#
profile detail
D

Demo User

Member since 2024

Bio
Set a hash and click Navigate to render it here.
How the DOM rendering works
// VULNERABLE — innerHTML parses HTML element.innerHTML = hash;
Real-world relevance

eBay DOM XSS (2014)

In 2014, security researcher Patrik Hultqvist discovered a DOM-based XSS vulnerability in eBay's listing description page. eBay's server-side code rendered the listing safely, but a JavaScript widget on the page read the URL hash and wrote it into the DOM via innerHTML. An attacker could craft a URL like /item/123#<script>… and the payload would execute in the context of the eBay domain. Because the hash fragment never reaches the server, eBay's server-side filters and WAF could not detect or block the attack.

This case highlights a critical blind spot: server-side security measures — input validation, output encoding, WAF rules — are invisible to client-side JavaScript that reads from sources the server never sees. Modern single-page applications that rely on hash-based routing are particularly susceptible because every route change triggers DOM manipulation from a client-side source.

javascriptvulnerable
// VULNERABLE — reads hash and writes to innerHTML
const name = location.hash.slice(1);
document.getElementById('greeting').innerHTML =
  'Hello, ' + name + '!';

// SAFE — uses textContent instead of innerHTML
document.getElementById('greeting').textContent =
  'Hello, ' + name + '!';

// SAFE — sanitise before using innerHTML
import DOMPurify from 'dompurify';
document.getElementById('greeting').innerHTML =
  'Hello, ' + DOMPurify.sanitize(name) + '!';
Mitigation

Avoid sinks. Validate sources.

The most effective defence against DOM XSS is to avoid dangerous DOM sinks altogether. Replace innerHTML with textContent and document.write with safe DOM manipulation APIs. When sinks are unavoidable — for example, rendering rich text — pass the value through a dedicated sanitisation library like DOMPurify that strips dangerous tags and attributes.

Treat every client-side source as untrusted. Validate URL fragments against a whitelist of expected formats before using them in any DOM operation. For hash-based routing in SPAs, decode and validate the path component before rendering it. And because third-party JavaScript runs with the same privileges as first-party code, vet every script your application loads — a compromised CDN or widget can introduce DOM XSS without touching your own code.

Further reading
  • OWASP — DOM-based XSS(OWASP)
  • eBay DOM XSS Disclosure (2014)(Patrik Hultqvist)
  • CWE-79 — Improper Neutralisation of Input During Web Page Generation(MITRE)
Key takeaways

What to remember

  • DOM XSS is purely client-side — the payload never reaches the server.
  • URL hash fragments are invisible to server-side logging, WAFs, and most security scanners.
  • Replace innerHTML with textContent wherever possible.
  • When sinks are unavoidable, sanitise with a library — never build your own filter.
  • The eBay 2014 case shows that server-side defences cannot protect against client-side injection from untrusted sources.

Knowledge check

0/3 answered · 0 correct
  1. 1.What makes DOM XSS different from reflected and stored XSS?

  2. 2.Which of the following is a DOM XSS sink?

  3. 3.Why was the eBay 2014 DOM XSS vulnerability difficult for server-side defences to detect?