innerText respects CSS visibility and rendered formatting
The innerText property sets or gets the visible text content of an element, respecting CSS styling such as ignoring text in display: none elements. It reflects rendered text formatting including line breaks and spacing.
innerHTML vulnerable with untrusted data
Using innerHTML with untrusted data from API responses or user input can execute malicious JavaScript in the user's browser, leading to XSS vulnerabilities. Potential risks include stealing session cookies, defacing the website, redirecting users to malicious sites, performing unauthorized actions, and keylogging user inputs.
innerHTML acceptable only with static hardcoded HTML
innerHTML may be used cautiously only for small, fixed HTML snippets that are part of your application's source code and contain no user input. Example: document.getElementById('footer').innerHTML = '<p>© 2025 My Company. All rights reserved.</p>';
innerHTML with sanitized HTML using DOMPurify
For user-generated HTML such as in rich text editors, sanitize with a library like DOMPurify before using innerHTML. Example: import DOMPurify from 'dompurify'; const userInput = '<img src=abc onerror=alert("xss")>'; document.getElementById('content').innerHTML = DOMPurify.sanitize(userInput);
innerHTML alternatives: templating engines and modern frameworks
Prefer templating engines with auto-escaping for reusable, structured HTML snippets. Use modern frameworks like React, Vue, Angular, or Svelte for complex applications as they standardize DOM manipulation, provide reactivity, and inherently handle sanitization for dynamic data. However, avoid unsafe APIs like dangerouslySetInnerHTML in React or [innerHTML] in Angular to prevent XSS vulnerabilities.
textContent for safe text-only DOM updates
The textContent property sets or gets the plain text content of an element and treats inserted HTML tags as literal text without parsing them. It is ideal for displaying text-only content from APIs or user inputs. Example: document.getElementById('content').textContent = userInput;
textContent vs innerText selection
Use textContent in monolithic applications to safely insert plain text content returned from APIs. Use innerText only when CSS visibility or rendered text formatting is required. textContent is slightly faster and more predictable; use it unless you need to respect rendered text formatting.
textContent and innerText do not protect against all XSS contexts
While textContent and innerText are safe for inserting plain text into the DOM, they do not protect against XSS in other contexts such as HTML attributes, JavaScript event handlers, or URLs. Always validate and sanitize untrusted input.
DOM-based data skimming - avoid rendering sensitive data in page DOM
Avoid rendering any sensitive information directly into a web page's DOM. Instead, display sensitive data in UI elements that are isolated from the web page's context and controlled by the extension. Use secure alternatives: Popup (display information in a popup UI when user clicks extension icon), Options Page (dedicated page for user-specific data), or Side Panel (persistent UI in separate pane, isolated from page content).
DOM-based data skimming - Shadow DOM is not sufficient isolation
Even using a Shadow DOM for encapsulation may not be sufficient safeguard because page scripts can query an 'open' Shadow DOM. Even a 'closed' Shadow DOM is not safe if other browser extensions are threats, because extensions can pierce through using openOrClosedShadowRoot() API. Using truly separate extension-controlled UIs is the most reliable mitigation.
Prototype-based data skimming - avoid sensitive data in injected scripts
Do not use the web page's context when sensitive user information is handled. If communication with scripts in the web page's context is necessary, use only non-sensitive, essential information. For example, pass just a result of validation instead of the whole secret token. Do not assume native prototypes can be safely obtained through tricks, as bypasses are frequently invented. Even at document_start timing, context of newly created iframes can be tweaked by web page scripts before extension script starts.
Prototype-based data skimming - vulnerability mechanism
Prototype pollution and prototype overriding attack: malicious webpages can overwrite global objects (built-in objects, primordials, prototypes) in their context to steal data. If an extension's injected script uses these overwritten objects with sensitive data, it inadvertently triggers malicious code leading to data exfiltration.