XSS in Modern React
On this page
What React Protects by Default
- React escapes text nodes by default, reducing classic injection risks.
- This does not remove XSS. XSS can still occur via unsafe HTML, dangerous URLs, and third party code.
High Risk Vectors
- Unsafe HTML injection: rendering untrusted HTML content.
- URL injection: untrusted values placed into href, src, or navigation.
- Attribute misuse: untrusted values used in style or data attributes then interpreted by other code.
- Dependency compromise: malicious code in packages executes in the client.
Unsafe HTML Example
// High risk when html is untrusted
function Dangerous({ html }: { html: string }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}
Safer Approach
- Prefer rendering as text, not markup.
- If HTML is required, sanitize with a strict allowlist and add tests for bypass attempts.
- Keep sanitized content isolated and never mix with dynamic script insertion.
URL and Navigation Controls
- Validate protocols for outbound links. Allow https and mailto only if required.
- Block javascript and data URLs in user controlled link fields.
- Encode query params and never concatenate raw strings into URLs.
Content Security Policy
- CSP reduces impact of XSS by restricting script sources.
- Use nonces or hashes for scripts if inline is unavoidable.
- Monitor CSP violation reports to detect injection attempts.
Operational Failure Modes
- HTML sanitization allowlist too broad, enabling script execution via edge tags.
- Trusted component later reuses sanitized HTML in a different context and breaks assumptions.
- URL values not validated and allow javascript protocol execution.
- Dependency update introduces malicious code path.
Production Checklist
- Inventory all unsafe HTML usage and document justification.
- Sanitize untrusted HTML with strict allowlist and test bypass cases.
- Validate outbound URLs and restrict protocols.
- Deploy CSP and monitor violations.
- Lock dependencies and audit changes continuously.