XSS in React — dangerouslySetInnerHTML and the specific bugs we see
React escapes interpolations by default, which eliminates 95% of XSS. The remaining 5% kills apps. Here are the exact patterns that slip through.
React's JSX is XSS-safe for variable interpolations. But `dangerouslySetInnerHTML`, certain `href` patterns, and user-controlled React children can re-introduce XSS. This guide covers the five patterns we see most in AI-built apps.
What it is
Cross-site scripting lets an attacker inject JavaScript that runs in other users' browsers. In React, the default text interpolation is safe. XSS requires opting out of the defaults.
Vulnerable example
// Pattern 1: dangerouslySetInnerHTML with untrusted content
<div dangerouslySetInnerHTML={{ __html: userBio }} />
// Pattern 2: javascript: URL in href
<a href={userLink}>{userLink}</a>
// user submits: javascript:alert(document.cookie)
// Pattern 3: React children from JSON.parse
<div>{JSON.parse(serverData).children}</div>Fixed example
// 1. Sanitize HTML input with DOMPurify
import DOMPurify from "isomorphic-dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userBio) }} />
// 2. Validate the URL protocol
function safeHref(u: string) {
try { return /^https?:$/.test(new URL(u).protocol) ? u : "#"; }
catch { return "#"; }
}
<a href={safeHref(userLink)}>{userLink}</a>
// 3. Never render JSX from user-controlled structures
<div>{String(serverData).slice(0, 1000)}</div>How Securie catches it
Securie's XSS specialist flags every dangerouslySetInnerHTML call with user-path input, every href/src binding to user data, and every SSR path that embeds attacker-controlled JSON without escaping.
Checklist
- dangerouslySetInnerHTML only used with DOMPurify-sanitized content
- URL props validated for http/https protocols
- User-controlled data never builds React element structures
- Strict Content-Security-Policy present (defense in depth)
- Server-rendered JSON escaped to avoid closing </script> injection
FAQ
Does React handle everything?
React handles text. It does not handle HTML strings, URL protocols, or user-controlled object structures. Those three are where XSS comes from in React apps.