What Is the DOM? Document Object Model Explained
A foundational guide to the Document Object Model (DOM): how browsers represent HTML as a tree, how JavaScript interacts with it, and why it matters for security.
Short answer
The DOM (Document Object Model) is the browser's internal representation of a web page — a tree structure built from HTML that JavaScript can read, modify, and delete in real time. When you see a page update without a full reload, the DOM is changing.
The idea in one minute
Imagine a restaurant kitchen with a whiteboard showing every table's order on sticky notes. The head chef writes the initial orders. A waiter can read any note, change an order ("no onions on table 4"), add a new note, or remove a finished one. Everyone looking at the board sees changes instantly.
The whiteboard is the DOM. The sticky notes are the HTML sent by the server. The waiters are JavaScript. The DOM is not the HTML file — it is a live, mutable data structure the browser builds and keeps in sync with what you see on screen.
How the DOM is structured
The browser parses HTML into a tree of nodes. For <div id="content"><h1>Title</h1><p class="intro">Text</p></div>, the tree is: div#content with children h1 (text node "Title") and p.intro (text node "Text"). Every node is an object with properties like .textContent, .className, .style and methods like .appendChild(), .removeChild().
The request flow
The DOM is not fetched from the server. The server sends HTML; the browser parses it into the DOM in memory. JavaScript can then modify the DOM without any network request. There is no round trip — every DOM change is instant and invisible to the server.
A minimal example
<div id="output"></div>
<script>
const name = location.hash.slice(1);
document.getElementById("output").innerHTML = name;
</script>
Visiting https://example.com/page#<img src=x onerror=alert(1)> causes the attacker's HTML to execute. The server never saw the payload — it is in the fragment, which is never sent to the server. This is DOM-based XSS: the entire attack exists in client-side code.
Why the attack surface exists
The DOM is an execution environment. When JavaScript writes user-controlled data into the DOM using APIs like innerHTML, it is not just changing the page's appearance — it is asking the browser to parse and execute the string as HTML. The browser trusts its own APIs. If an attacker's string reaches innerHTML, the browser treats it as legitimate code because the browser has no concept of "this string came from an attacker." Trust is inherited from the API, not validated from the content.
What attackers look for
DOM sources of attacker data: location.hash, location.search, document.URL, document.referrer, window.name, postMessage events, localStorage keys set by other origins. DOM sinks that interpret data as code: innerHTML, outerHTML, document.write(), insertAdjacentHTML(), eval(), setTimeout(string), new Function(). An attacker traces any source reaching any sink without sanitization.
Detection
Identify all DOM sources and sinks in client-side JavaScript. Trace data flows manually or with an automated DOM scanner. Check for innerHTML, document.write, and eval receiving user-controllable values. Look for third-party scripts that read location.hash or postMessage data.
Verification: real vulnerability or false positive?
Confirm the sink is reachable and the code path is not conditional on user interaction the scanner missed. Check if Content-Security-Policy blocks inline script execution. Test in a real browser — automated DOM simulators parse JavaScript differently from Chrome or Firefox. Distinguish DOM-based XSS (payload never reaches server, e.g., in the fragment) from reflected XSS (payload appears in the server response).
Real-world impact
DOM-based XSS via fragment bypasses every server-side defense — WAF, input filter, SQL sanitizer — because the payload is never sent to the server. An attacker sends a crafted link; the victim's browser executes the payload; the server never knows. DOM clobbering in a third-party widget propagates to every site embedding it. postMessage origin confusion lets an iframe manipulate the parent page's DOM.
Prevention
Use textContent instead of innerHTML, setAttribute over string-based attribute construction. Sanitize HTML with DOMPurify when HTML insertion is unavoidable. Validate data from location.hash and postMessage before use. Set Content-Security-Policy to restrict inline scripts and eval(). Avoid dangerouslySetInnerHTML (React) and v-html (Vue).
Related vulnerabilities
- DOM-based XSS — attacker data written into HTML-context sinks
- DOM clobbering — HTML elements shadow JavaScript variables via
idattributes - Clickjacking — DOM-level iframe embedding tricks users into clicking invisible buttons
- Prototype pollution — modified
Object.prototypechanges DOM behavior
Testing methodology (do this safely)
Identify all sources and sinks in client-side JavaScript. Trace data flows manually or with an automated DOM scanner. Confirm exploitation in a real browser. Test only on your own sites or authorized targets.
Further reading
- MDN: Document Object Model (DOM)
- W3C: Document Object Model (DOM) Level 2 Core Specification
- PortSwigger: DOM-based vulnerabilities
- MITRE: CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Nyxeara perspective
Server-side vulnerability scanners miss DOM-based attacks because the payload never reaches the server. The Nyxeara engine combines server-side analysis with client-side DOM simulation — it traces data flows from browser-side sources to dangerous sinks within the JavaScript execution context. A parameter reflected in innerHTML inside a client-side script is invisible to a proxy-based scanner but detectable by an engine that models the DOM as an attack surface.