How DOM Clobbering Overrides Global Variables

DOM clobbering is a browser behavior and security vulnerability where HTML elements with specific id or name attributes inject properties into the global window object or document namespace. When web applications rely on global variables or object configurations without strict initialization, attackers can inject seemingly harmless HTML markup to overwrite these variables. This article explains the browser mechanics behind DOM clobbering, how JavaScript resolves named elements as globals, how nested clobbering works, and how to defend against it.

The Browser Legacy: Window Object Property Resolution

To maintain backwards compatibility with legacy web standards, modern browsers automatically map HTML elements containing id or name attributes directly to properties on the global window object.

For example, consider the following HTML markup:

<form id="config"></form>

When this element is rendered, the browser automatically creates a property on window:

console.log(window.config); // Returns [object HTMLFormElement]
console.log(config);        // Also references the same element directly

Because window is the global execution context in client-side JavaScript, any variable reference that is not declared in the local scope via var, let, or const falls back to checking the properties of window.

How Global Variable Overriding Occurs

DOM clobbering occurs when an application checks for the existence of a global configuration object or property that has not yet been explicitly assigned in JavaScript, or when the application accesses properties directly from window.

Consider a vulnerable script pattern:

// The application checks for an existing config object
var apiEndpoint = window.config ? window.config.url : "/default/api";

If an attacker injects HTML prior to this script’s execution:

<a id="config" href="https://attacker.com/malicious-api"></a>

The browser maps the <a> element to window.config. Because the HTML anchor tag has a built-in href property that converts to a string representation via .url or .href, accessing window.config evaluates to the DOM element instead of undefined. Consequently, the application reads the attacker-supplied URL instead of falling back to the default path.

Multi-Level (Nested) Clobbering

Attackers are not limited to single-level global properties. By combining elements such as <form> tags with named children, or using collections of elements sharing the same name, attackers can clobber multi-level objects like window.user.profile.

1. Form Child Relationship

Form elements automatically expose their child inputs as properties using their name attributes:

<form id="user">
  <input name="role" value="admin">
</form>

In JavaScript, evaluating window.user.role.value will yield the string "admin", overriding an uninitialized user object.

2. Grouped Elements (HTMLCollection)

If multiple elements share the same id or name, the browser groups them into an HTMLCollection:

<a id="user" name="profile" href="https://evil.com/payload.js"></a>
<a id="user"></a>

Here, window.user resolves to an HTMLCollection, and window.user.profile accesses the first link’s attributes.

Why JavaScript Scope Does Not Always Protect

JavaScript scoping rules determine what gets clobbered:

Preventing DOM Clobbering

  1. Use Strict Variable Declarations: Always declare variables using const or let to prevent uninitialized identifiers from falling back to window.

  2. Explicit Type and Instance Checking: Verify that properties are instances of the expected object types rather than relying solely on truthy checks:

    if (window.config && !(window.config instanceof HTMLElement)) {
        // Safe to use config
    }
  3. HTML Sanitization: When parsing user-controlled markup, use sanitization libraries configured to strip or neutralize id and name attributes that could interfere with application logic.

  4. Use Object.freeze() or Explicit Initialization: Define global configuration namespaces explicitly in an immutable format before rendering dynamic or user-generated HTML.