Loading...

Dom-based Vulnerabilities

Understanding the Document Object Model (DOM)

The Document Object Model (DOM) represents HTML, XML, or SVG documents as a tree structure of objects in memory. Browsers parse markup into this live model, enabling JavaScript to dynamically access, modify structure, content, styles, and events.

Tree Structure

The DOM forms a hierarchical tree with the document node as root, branching into elements, attributes, text nodes, and comments as child nodes. Each node exposes properties (e.g., nodeName, childNodes) and methods (e.g., appendChild(), removeChild()) for traversal and manipulation.

Key Interfaces

Core DOM provides language-neutral APIs; HTML DOM extends for browser-specific features like getElementById() or querySelector(). Event handling attaches listeners to nodes via addEventListener(), firing on user interactions or DOM changes.

Dynamic Nature

Unlike static HTML, the DOM remains mutable post-load—scripts can insert/delete nodes, alter attributes, or trigger reflows/repaints. Mutations fire events like DOMNodeInserted, enabling reactive UIs but risking performance issues from frequent changes.

Insecure Data Flow Vulnerabilities

Insecure data flow vulnerabilities arise when untrusted or sensitive data moves through an application without proper validation, sanitization, or access control, ultimately reaching dangerous “sinks.” These flaws are typically modeled with taint-style analysis: data from an untrusted source becomes “tainted” and, if not neutralized, can drive security‑critical operations.

Core concept

Data-flow vulnerabilities focus on how information travels from an entry point (source) to a sensitive operation (sink), potentially crossing multiple functions and layers. When this flow is not controlled, attackers can influence behavior such as command execution, database access, file I/O, or network calls.

Sources, sinks, and propagation

Untrusted sources include user input, HTTP parameters, cookies, headers, request bodies, and external services or files. Sinks are security‑sensitive operations like SQL execution, OS commands, HTML rendering, deserialization, or internal network/file access. Propagation occurs as variables are passed through functions and data structures; if taint is not removed by validation or sanitization, the vulnerability remains exploitable.

Typical vulnerability types

Many injection bugs are specific instances of insecure data flow, such as SQL injection (tainted input reaches query builders), XSS (tainted data sent to HTML/JS sinks), and insecure deserialization (untrusted serialized data fed into object deserializers). Server-side request forgery and path traversal similarly arise when unvalidated user-controlled URLs or paths are used in network or filesystem APIs.

Security impacts

Insecure data flows can compromise confidentiality (leaking secrets), integrity (modifying data or behavior), and availability (denial-of-service via malicious input). At higher privilege levels (e.g., in kernels or critical backends), tainted flows can lead to memory corruption, remote code execution, or full system compromise.

Detection and mitigation

Static and dynamic taint analysis tools track how data flows from sources to sinks, flagging paths where no sanitization or checks occur. Robust defenses include strict input validation at trust boundaries, context-appropriate output encoding, least-privilege design at sinks, and architectural reviews to ensure that sensitive operations never depend directly on untrusted data.

High-Risk DOM Sinks in Client-Side Code

High-risk DOM sinks are JavaScript and DOM APIs that will interpret data as HTML, URLs, or executable code when fed with untrusted input. These sinks are the critical endpoints in DOM-based XSS and related client-side vulnerabilities.

Classic code-execution sinks

eval(), Function(), setTimeout(), setInterval() when used with string arguments can execute attacker-controlled JavaScript directly.
document.write() / document.writeln() inject arbitrary markup into the document, allowing script execution if untrusted data is concatenated into the written HTML.

HTML and DOM injection sinks

element.innerHTML, outerHTML, and insertAdjacentHTML() parse strings as HTML, enabling injection of elements with event handlers or JavaScript URLs.
Setting event handler properties (such as element.onclick = userData) or attributes via setAttribute('onclick', userData) can turn attacker-controlled strings into executable handlers.

URL and navigation sinks

Assigning untrusted data to navigation-related properties or attributes, such as location.href, element.href, or form action/method attributes, can trigger javascript: URLs or open attacker-controlled pages in privileged contexts.
History APIs history.pushState() / replaceState() with attacker-controlled URLs can be used as part of complex DOM gadget chains when combined with other sinks.

Other dangerous assignments

Manipulating script-related properties, for example script.src or script.text, with untrusted data can load or define attacker-supplied code.
Using CSS/DOM APIs like element.style.cssText or style.backgroundImage with attacker-controlled values can be abused in some browser/feature combinations, especially when combined with URL-based payloads or legacy features.

Safer alternatives

Where possible, prefer text-only sinks like textContent or innerText, which treat input as literal text instead of HTML or code. Combined with strict input validation, output encoding, and Content Security Policy (including Trusted Types), this greatly reduces the exploitability of high-risk DOM sinks.

DOM Object Hijacking

DOM object hijacking is a client‑side vulnerability where an attacker manipulates JavaScript‑exposed objects in the DOM so that legitimate code later uses those objects in a malicious, attacker‑controlled way. It is essentially “steering” existing front‑end logic by corrupting the data it trusts in the browser environment.

Core idea

Modern apps keep lots of state in DOM‑reachable objects: configuration blobs, routing objects, user context, JSON responses, or values derived from location, storage, or messages. When any of these are controllable from outside (URL, postMessage, injected script, extension, etc.) and then consumed by security‑sensitive logic (navigation, templating, eval‑like behavior, API calls), the attacker can hijack that object’s contents to alter application behavior. This is conceptually close to DOM‑based XSS: untrusted data travels through a “DOM object” and ends up in a dangerous sink.

Typical patterns

State / config object takeover: Global objects used for routing, feature flags, or widget config are filled or updated from window.location, fragment, or JSON without validation; hijacking them can redirect users, flip flags, or change endpoints.
JSON / data hijacking: Client reads server JSON into an object, then passes fields into HTML/JS sinks like innerHTML, document.write(), or eval(); if an attacker can control those fields (via upstream input or MITM on insecure transport), the object effectively becomes a vehicle for script injection.
Library / framework objects: Some DOM gadgets arise when libraries (for example, old jQuery patterns) take objects from location or hash and then feed them into selector or HTML APIs, letting an attacker control both selector and DOM injection target.

Security impact

Once an attacker controls such objects, they can often:
Achieve DOM‑based XSS by steering values into execution/HTML sinks.
Force client‑side redirects or open phishing flows by corrupting navigation objects that write to location.
Exfiltrate data by modifying callback / handler objects that receive sensitive JSON or DOM references.

Hardening strategies

Defending against DOM object hijacking means treating every object that can be influenced from outside as untrusted until proven otherwise. Validate and normalize all data from URL, storage, postMessage, and JSON before merging it into global state objects, and avoid piping object properties into high‑risk sinks (innerHTML, eval, new Function, document.write) without strict encoding or safer alternatives (textContent, safe templating).

Mitigation Strategies for DOM-Based Taint-Flow Issues

Mitigating DOM-based taint‑flow issues means ensuring that attacker‑controllable data from DOM “sources” can never reach dangerous “sinks” without strict validation, encoding, or safe APIs.

1. Break unsafe source → sink paths

Identify all untrusted sources such as location (search, hash), document.referrer, postMessage, cookies, localStorage, and user input elements.
For each source, ensure data never flows directly into high‑risk sinks (innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, new Function, setTimeout/setInterval with strings, event handler attributes, javascript: URLs).

2. Prefer safe DOM APIs

Use text‑only properties like textContent / innerText instead of innerHTML when rendering user data.
Use createElement, setAttribute, and property assignments instead of string‑built HTML, and avoid constructing script or event attributes dynamically.

3. Validate and sanitize untrusted data

Apply strict input validation on client‑side data used for DOM updates, URLs, or logic (whitelists for expected formats like IDs, enums, numeric ranges).
For cases where HTML has to be accepted (e.g., rich text), sanitize with well‑maintained libraries such as DOMPurify before inserting into the DOM.

4. Use browser and platform protections

Enforce a strong Content Security Policy (CSP) to disallow inline scripts and unsafe-eval, and to restrict script origins.
Consider CSP Trusted Types for critical apps to ensure only vetted code paths can create HTML/script strings for risky sinks.

5. Analyze and monitor taint flows

Use developer tools and breakpoints to observe how data propagates from sources to sinks at runtime when testing for DOM XSS.
Integrate dynamic taint‑tracking or DOM‑aware scanners (e.g., browser‑based taint engines, DOM‑XSS‑focused tools like DOM Invader / research prototypes) into security testing pipelines to catch hidden flows.

6. Reduce client-side business logic reliance

Avoid making critical security decisions purely in client‑side code that depends on attacker‑controllable DOM state; mirror or enforce checks on the server.
Regularly review third‑party scripts and client-side dependencies, as they can introduce new tainted flows and unsafe sinks outside first‑party control.