Join our Newsletter — 33% off our NHI Course
Home› Guides› DOM Clobbering Guide: Finding and Preventing HTML That…
Guide Web & API Security

DOM Clobbering Guide: Finding and Preventing HTML That Hijacks Scripts

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 14 November 2026 13 min read
On this page

DOM clobbering lets markup that contains no script at all change what your JavaScript reads, so an application that strips scripts and event handlers from user content can still end up loading code from an attacker's server. It works because browsers expose elements with id and name attributes as named properties of window and document, and those properties can shadow variables and browser APIs that page scripts expect. The risk is not theoretical: in 2024 the build tools webpack, Rollup and Vite each shipped fixes for generated code that a clobbered lookup could redirect. This guide explains the mechanism at the level a defender needs, how to find exposed code, and the layered controls that prevent it. It is written for front-end engineers, application security teams and assessors, and it deliberately contains no working attack markup.

Key takeaways

  • Scriptless HTML is not harmless HTML. If user content can carry id or name attributes, it can interfere with script logic on the same page.
  • Sanitise the attributes, not just the tags. Configure the sanitiser to remove or namespace id and name on untrusted markup; DOMPurify's SANITIZE_NAMED_PROPS is built for this.
  • Stop reading globals. Code that reads configuration from window or document properties, especially with an || fallback, is the pattern clobbering exploits.
  • Check types before trusting values. A clobbered value is an Element or HTMLCollection, so an instanceof or type check catches it.
  • Keep dependencies and bundles current. Generated runtime code from bundlers has been vulnerable, so patching the toolchain and rebuilding is part of the fix.

What DOM clobbering is and why sanitisers miss it

The WHATWG HTML Standard defines "named access on the Window object". The supported property names of a Window include the id of any element in the document, the name of embed, form, img and object elements, and the target names of child browsing contexts such as iframes. When one element matches, the property returns that element; when several match, it returns an HTMLCollection. The standard itself warns that relying on this leads to brittle code, because the set of names can change as features are added, and recommends document.getElementById() or document.querySelector() instead.

OWASP's DOM Clobbering Prevention Cheat Sheet describes the security consequence: named element references take precedence over built-in APIs and developer-defined properties on window and document. If a script reads a global that the developer expected to be undefined, or a property on an object that was expected to hold a configuration value, injected markup can supply an element in its place. Because many elements convert to a string through attributes such as a link's href, the script may then use an attacker-chosen URL as a script source, redirect target or API endpoint.

PortSwigger's Web Security Academy lists the conditions that make it exploitable:

  • Controlled markup: the attacker can place HTML on the page but cannot run script directly.
  • Permitted attributes: the filter allows id or name.
  • A gadget in the code: page JavaScript reads a global or object property that may be undefined, often through an || fallback.
  • Unchecked DOM reads in filters: client-side filtering code reads DOM properties without confirming what they are, which can let clobbering bypass the filter itself.

This is why a sanitiser that correctly removes <script>, event handlers and javascript: URLs can still leave a page exposed. The markup it lets through is valid, harmless-looking and scriptless; the vulnerability is in the page's own code. For the sanitiser fundamentals see the HTML Sanitisation Guide.

Where it shows up: real-world gadgets

The most instructive cases are in code developers did not write themselves. In 2024 three widely used JavaScript bundlers published advisories for DOM clobbering in the runtime code they generate. Each required a page that also rendered attacker-influenced, scriptless HTML with unsanitised name or id attributes, for example from comments, forums, markdown renderers or web mail.

AdvisoryAffected codeConditionFixed in
CVE-2024-43788 (GHSA-4vvj-4cpr-p986), webpackAutoPublicPathRuntimeModule, which works out the base URL for loading further chunksoutput.publicPath unset or "auto"; the runtime's lookup of the current script could be shadowed, so later chunks load from an attacker-chosen base path. The advisory reports a real-world instance in Canvas LMS5.94.0
CVE-2024-47068 (GHSA-gcx4-mw62-g8wm), Rollupimport.meta handling that resolves URLs from document.currentScriptcjs, umd or iife output using import.meta or plugin-emitted assets2.79.2, 3.29.5, 4.22.4
CVE-2024-45812 (GHSA-64vr-g452-qvp3), ViteAsset URL resolution relative to document.currentScriptcjs, iife or umd output that loads assets by relative path5.4.6, 5.3.6, 5.2.14, 5.1.8, 4.5.5, 3.2.11

All three were rated Moderate (CVSS 3.1 base score 6.4) and classified as CWE-79. The lesson for defenders is that the vulnerable lookup sat in build output, not application source, so a code review of your own files would not find it. Software composition analysis, prompt toolchain updates and a rebuild of affected bundles are required; see the Dependency Update Automation Guide.

Typical gadget patterns in application code, described here so reviewers can recognise them:

  • Global configuration with a fallback: code that uses a global configuration object if it exists and a default otherwise.
  • Feature detection by property presence: code that checks whether a browser API or library global exists before using it; a clobbered name makes the check pass with an element.
  • document API shadowing: code that calls a document method or reads a document property after untrusted markup has been inserted; a named form or image can replace it.
  • Form property shadowing: scripts that read a form's action, attributes or submit when the form contains user-controlled inputs whose names collide.
  • Client-side sanitisers that walk the DOM: filters that read attributes or childNodes without confirming the value is a NamedNodeMap or NodeList.

Prevention controls

No single control is sufficient. The table maps each defence to what it stops and where it falls short, drawing on the OWASP cheat sheet, PortSwigger guidance and the DOMPurify documentation.

ControlWhat it stopsLimits
Sanitiser removes or namespaces id and nameUntrusted markup creating named properties at allBreaks in-page anchors and form names in user content unless namespaced; only covers markup that passes through the sanitiser
DOMPurify SANITIZE_DOM (on by default)Collisions with built-in DOM APIs and propertiesDoes not protect developer-defined globals
DOMPurify SANITIZE_NAMED_PROPS: true (off by default)Collisions with developer-defined variables, by prefixing id and name values with user-content-Links and CSS that target the original id must be updated
HTML Sanitizer API with removeAttributes for id and nameSame as above, natively in supporting browsers via setHTML()Limited browser availability: fall back to DOMPurify, never to unsanitised insertion
Explicit declarations (const, let), strict mode, local scope, encapsulationImplicit globals that clobbering can supplyCode that deliberately reads window.X remains exposed
Type checks (instanceof, checking for DOM nodes) before sensitive useAn element or collection being used as a string, URL or objectMust be applied at every sensitive read
Strict Content Security Policy script-srcClobbered script URLs loading code from origins outside the allow listDoes not stop clobbered values reaching existing sinks such as eval() or same-origin script paths
Trusted TypesClobbered strings reaching DOM XSS injection sinks without passing a policyRequires policy design and browser support; does not change what the code reads
Object.freeze() on app-owned configuration held in local scopeLater modification of trusted configuration objectsShallow; never freeze window, document or DOM elements (OWASP notes freezing window throws)

Sanitiser configuration: where user content does not need id or name, remove them. Where it does, for example heading anchors in rendered markdown, namespace them with DOMPurify's SANITIZE_NAMED_PROPS option and update the links that target them. Keep SANITIZE_DOM on; the DOMPurify README marks disabling it as a minor XSS risk. With the HTML Sanitizer API, build a Sanitizer that removes the two attributes and pass it to setHTML(). MDN currently marks the API as limited availability, so feature-detect it and keep DOMPurify as the fallback.

Code patterns: read configuration from module scope or an explicitly passed object, not from window. Replace "global or default" fallbacks with explicit initialisation. Use document.getElementById() and querySelector() rather than named access, as the HTML Standard advises. Before a value is used as a URL, script source or HTML, confirm its type. In client-side filters, confirm that attributes is a NamedNodeMap before iterating it.

Browser policy layers: a nonce- or hash-based CSP limits where a clobbered script URL can load from, and Trusted Types forces strings through a reviewed policy before they reach injection sinks. See the Content Security Policy Guide and the Trusted Types and DOM XSS Prevention Guide.

Choosing controls by application type

ApplicationExposurePriority controls
Renders rich user content (comments, wikis, web mail, markdown, support tickets)High: untrusted markup sits beside application scriptsNamespace or strip id and name; bundler patches; strict CSP; type checks in shared code
Embeds third-party widgets or scripts that inject HTMLMedium: third-party HTML injection can supply clobbering markup, as the bundler advisories noteInventory third-party scripts; CSP; isolate widgets in sandboxed iframes where possible
Single-page app with framework templating and no raw HTML insertionLower: frameworks escape by defaultAudit every raw HTML escape hatch; keep bundlers patched; avoid globals for configuration
Static marketing site with no user contentLowKeep dependencies current; CSP as baseline

A small team with one product can usually remove id and name from user content outright and patch its bundler in a sprint. A large organisation with many front ends should centralise sanitiser configuration in a shared library, so the decision is made once and enforced by tests, and track bundler versions across repositories through software composition analysis.

Implementation: a phased approach

  1. Find the inputs. List every feature that renders user-supplied or third-party HTML: rich text editors, markdown, email rendering, imported documents and widgets. These are the pages where clobbering markup can appear.
  2. Patch the toolchain. Check webpack, Rollup and Vite versions against the advisories above in every repository, upgrade, rebuild and redeploy. Where feasible, set webpack's output.publicPath explicitly, as the webpack advisory recommends.
  3. Tighten the sanitiser. Change the shared sanitiser configuration to remove or namespace id and name. Run it first against a corpus of real stored content in a test environment to find broken anchors and styles before release.
  4. Fix gadgets in your own code. Search for reads of window. and document. properties used as configuration, || fallbacks on globals, and named access. Replace them with explicit declarations, local scope and type checks.
  5. Add browser policy layers. Deploy CSP and Trusted Types in report-only mode, review violations, then enforce.
  6. Test and keep testing. Add regression tests that render untrusted content containing named attributes and assert the sanitiser output and the application's behaviour. Include clobbering in assessment scope for features that render user HTML.
  7. Roll back safely. Ship sanitiser changes behind a configuration flag so a rendering regression can be reverted without reopening the old setting everywhere; never roll back a bundler security patch to fix a build issue.

Detection and measurement

  • Stored content scans: periodically scan stored user content for id and name values that match known global names, browser API names or names used in your scripts. Matches show attempted or accidental collisions.
  • CSP and Trusted Types reports: violation reports for unexpected script origins or blocked sink assignments on pages with user content can indicate a clobbered URL being used.
  • Static analysis: lint for implicit globals and configure code review rules for global reads with fallbacks and for named access on window and document. See the AI-Assisted Code Review and AI SAST Guide.
  • Dependency monitoring: software composition analysis alerts for bundler and sanitiser advisories, with a time-to-patch target.

Metrics to report:

  • Percentage of user-content rendering paths using the shared sanitiser with named attribute protection.
  • Repositories on patched bundler versions, against the total in scope.
  • Number of open gadget findings (global reads with fallbacks, unchecked DOM reads) in front-end code.
  • Pages with user content covered by an enforced CSP and Trusted Types policy.
  • Median time from a published client-side library advisory to deployed fix.

Common failure patterns

  • Allowing id and name because they look inert. Sanitiser allow lists often keep them for anchors and forms without considering named access.
  • Turning off SANITIZE_DOM to fix a rendering issue. This removes the built-in collision protection for the whole application.
  • Patching the library but not rebuilding. The bundler fixes only take effect when affected bundles are regenerated and redeployed.
  • Relying on CSP alone. CSP limits new script origins but does not stop a clobbered value reaching an existing sink.
  • Freezing or overriding window properties as a fix. It throws or breaks code, and named access still takes precedence on undefined names.
  • Feature detection by property existence. A clobbered name makes an unsupported API look present.

Framework mapping

FrameworkReferenceHow it applies
OWASP ASVS 5.0V3.2.3 (Level 3)Requires avoiding DOM clobbering through explicit variable declarations, strict type checking, not storing globals on the document object and namespace isolation
OWASP ASVS 5.0V1.3.1 (Level 1)Untrusted HTML from WYSIWYG editors or similar sanitised with a well-known library
OWASP ASVS 5.0V3.4.3 (Level 2)Content-Security-Policy with object-src 'none', base-uri 'none' and an allow list, nonces or hashes
CWECWE-79 Improper Neutralization of Input During Web Page GenerationThe classification used in the webpack, Rollup and Vite advisories
NIST SP 800-53 Rev 5SI-10 Information Input Validation; SI-15 Information Output Filtering; SA-11 Developer Testing and EvaluationSanitiser configuration, output handling and regression testing
MITRE ATT&CK v20T1189 Drive-by Compromise; T1059.007 JavaScriptScript loaded through a clobbered URL runs in visitors' browsers

Practitioner checklist

  • List every feature that renders user-supplied or third-party HTML.
  • Upgrade webpack, Rollup and Vite past the 2024 advisories, then rebuild and redeploy.
  • Configure the shared sanitiser to remove or namespace id and name; keep DOMPurify's SANITIZE_DOM on.
  • Use the HTML Sanitizer API only with feature detection and a DOMPurify fallback.
  • Replace global configuration reads and || fallbacks with explicit, locally scoped declarations.
  • Add type checks before values are used as URLs, script sources or HTML.
  • Enforce a nonce- or hash-based CSP and Trusted Types on pages with user content.
  • Add regression tests and include clobbering in assessment scope for user-content features.

Standards and references

Related NHI Mgmt Group resources: HTML Sanitisation Guide · Client-Side Security Guide · Web Application Attacks Guide · Cross-Site Scripting (XSS) Prevention Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 14 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.