Description
React's dangerouslySetInnerHTML renders the supplied HTML directly into the DOM. Passing user input or other untrusted HTML to __html without sanitization can insert dangerous event handlers or malicious links and lead to XSS.
Potential impact
- Sessions or tokens may be stolen, and the DOM may be modified.
- Malicious scripts may run in the user's browser to make additional API calls or display phishing interfaces.
- HTML output features such as CMS content, Markdown conversion, and comment rendering may become attack surfaces.
Remediation
- Minimize
dangerouslySetInnerHTMLand use ordinary JSX output where possible. - When HTML rendering is necessary, use only output processed by an established HTML sanitizer such as DOMPurify.
- Do not pass user input directly to
__html.
Examples
Before
javascript
import React from "react";
export function Bad({ html }) {
return <section dangerouslySetInnerHTML={{ __html: html }} />;
}
After
javascript
import React from "react";
import DOMPurify from "dompurify";
export function Good({ html }) {
return <section dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />;
}
Explanation:
- Before: Passing unsanitized input to
dangerouslySetInnerHTMLcan cause XSS. - After: DOMPurify sanitizes the HTML before rendering, removing scripts and dangerous attributes.
Limits of striptags options
javascript
import React from "react";
import stripTags from "striptags";
export function UnsafeAllowedTags({ html }) {
return <section dangerouslySetInnerHTML={{ __html: stripTags(html, "<img>") }} />;
}
export function UnsafeReplacement({ html }) {
return <section dangerouslySetInnerHTML={{ __html: stripTags(html, [], "<img src=x onerror=alert(1)>") }} />;
}
export function DefaultStripping({ html }) {
return <section dangerouslySetInnerHTML={{ __html: stripTags(html) }} />;
}
Allowing tags may also leave dangerous attributes intact, and replacement strings can reintroduce HTML. Do not treat tag stripping as equivalent to HTML sanitization. If markup is unnecessary, ordinary JSX output with {html} is simpler.