Unsanitized innerHTML Security Vulnerabilities
Assigning unsanitized user input directly to the DOM via
innerHTML is a significant source of client-side web
vulnerabilities. When browsers parse HTML strings passed through this
property, any embedded scripts or executable markup run within the
context of the user's session. This article outlines the primary
security risks associated with unsanitized innerHTML
assignments—chiefly Cross-Site Scripting (XSS), credential theft, DOM
clobbering, and interface manipulation—and explains how to eliminate
these attack vectors.
DOM-Based Cross-Site Scripting (DOM XSS)
The most prevalent and dangerous consequence of unsanitized
innerHTML assignments is DOM-based Cross-Site Scripting.
While modern HTML5 specifications prevent inline
<script> tags inserted via innerHTML
from executing automatically, attackers routinely bypass this protection
using inline event handlers and error triggers.
For example, injecting payloads such as
<img src="invalid" onerror="alert(1)"> or
<svg onload="alert(1)"> will cause the browser to
parse the HTML, attempt to load the invalid resource, and immediately
execute the attacker's JavaScript via the event handler. Once script
execution is achieved, the attacker gains full control over the
client-side context of the application.
Session Hijacking and Data Exfiltration
When an attacker successfully executes arbitrary JavaScript through
an innerHTML injection, they operate with the same
permissions as the authenticated user. This access allows malicious
scripts to:
- Read Sensitive Storage: Access cookies not
protected by the
HttpOnlyflag, as well as tokens and sensitive data stored inlocalStorageorsessionStorage. - Exfiltrate Data: Transmit session tokens, private
user data, or application state to attacker-controlled servers using
background network requests (such as
fetch()orXMLHttpRequest). - Forge Actions: Perform unauthorized actions on behalf of the user, such as modifying account credentials, making unauthorized financial transactions, or altering profile details.
DOM Clobbering
Unsanitized innerHTML assignments can also lead to DOM
clobbering, an attack where injected HTML manipulates global JavaScript
variables and window properties.
Browsers automatically create references on the window
object for HTML elements with id or name
attributes. An attacker injecting markup like
<form id="config"><input name="apiUrl" value="//malicious-domain.com"></form>
can overwrite an existing window.config.apiUrl reference
used by legitimate application scripts. When application logic later
relies on this clobbered property to fetch scripts or endpoints, it can
be redirected to load attacker-controlled resources without directly
executing a script payload at the initial injection point.
Phishing and UI Defacement
Attackers can use innerHTML injections to alter the
visual presentation of a page dynamically:
- Credential Harvesting: Injecting fake login forms or modal dialogs that prompt users to re-enter their passwords or multi-factor authentication codes.
- Forced Redirections: Altering legitimate links or
modifying
window.locationto silently redirect the user to a phishing website. - Reputational Damage: Modifying visible page content, headers, or messages to deface the application or present misleading information to end users.
Safe Alternatives to innerHTML
To prevent these vulnerabilities, developers should avoid using
innerHTML for displaying untrusted or dynamic text. Safer
development patterns include:
- Using
textContentorinnerText: When rendering plain text, these properties ensure that the input is treated strictly as text rather than executable markup, rendering special characters harmless. - Safe DOM Construction: Use APIs such as
document.createElement(),Element.setAttribute(), andNode.appendChild()to build UI structures programmatically. - Robust Sanitization: If raw HTML must be supported, pass the markup through a dedicated, battle-tested sanitization library like DOMPurify prior to insertion.
- Trusted Types: Implement the Trusted Types API to
enforce strict type checking on DOM sinks, preventing raw strings from
being passed directly to
innerHTML.