HTML Sanitization Must Resolve Namespaces Before It Trusts a Tag
HTML Sanitization Must Resolve Namespaces Before It Trusts a Tag HTML sanitization looks like a list problem: keep some elements, remove the rest, and strip dangerous attributes. The browser does not see a flat list. I
HTML Sanitization Must Resolve Namespaces Before It Trusts a Tag
HTML sanitization looks like a list problem: keep some elements, remove the rest, and strip dangerous attributes. The browser does not see a flat list. It parses markup into a DOM with namespaces, tree structure, and context-dependent rules. A sanitizer that compares tag names as strings without preserving those distinctions can approve markup that the browser interprets differently.
The WHATWG HTML sanitization section makes the context explicit. The safe insertion methods are tied to the element or shadow root receiving content, and Document.parseHTML() produces a document rather than mutating the caller's page. That context matters because an element allowed in a normal HTML body may have different behavior inside an SVG subtree, a table, a template, or a shadow root.
Parse first, then decide
A safer sanitizer parses untrusted markup with the browser's own parser, walks the resulting node tree, and builds an allowlist around namespace, local name, attributes, and attribute values. The output is a DOM fragment, not a repaired string. Serializing the approved tree back to text and later reparsing it creates a second parser opportunity.
The HTML Standard calls out server-side reflected and stored XSS, DOM clobbering, script gadgets, and mutation XSS among the reasons sanitization is difficult. A string filter may miss a custom element whose prototype changes later, an attribute that becomes active after serialization, or a namespace that the first parser treated as text. The browser's parser is the source of truth for what a page will execute.
Server-side sanitizers still have a role. They should use a library that documents its parser and namespace model, and the application should reject unsupported namespaces rather than treating every colon-containing name as an ordinary HTML element. The client should apply the same policy where it inserts content. Two independent sanitizers with different allowlists are a recipe for drift.
A practical allowlist
Begin with the smallest set of elements that the content model requires. Text, paragraphs, links, lists, and a few semantic elements are often enough. If images are needed, restrict schemes and handle URL resolution separately. Disallow raw style attributes unless the application has a dedicated CSS policy. Remove event-handler attributes and elements that can load active content.
For SVG and MathML, decide whether those namespaces are supported at all. If they are, validate them as separate trees. SVG can contain paint servers, use references, animation, and external resource links. A single svg allowance that inherits the HTML attribute policy is not a namespace-aware decision.
Attribute policy needs the same treatment. An href can carry a safe web URL or a dangerous scheme; a srcset can contain multiple candidates; an id can collide with application code; a name can alter form behavior. Normalize and validate the parsed value, then let the browser serialize it. Do not rely on case folding or regular expressions to infer namespace identity.
Review the insertion sink
The sanitizer is only one half of the control. Review the sink that receives its output. innerHTML, insertAdjacentHTML(), and framework template helpers can create a new parsing context. A safe fragment inserted into the wrong place may still produce unexpected markup. Prefer DOM construction and text nodes for values that should never be markup.
Finally, test with the browser version that users actually run. A parser behavior that changes between engines is security-relevant when the application trusts the output. Store the sanitizer version, the policy, and the test corpus with the deployment. A sanitizer should be treated as a security dependency with an update process, not as a utility that is correct because it removed a few script tags.
References
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.