
Preventing XSS Attacks in React Applications
Preventing XSS Attacks in React Applications
As a React developer, you build dynamic, user-friendly interfaces but without proper safeguards, your app could be vulnerable to Cross-Site Scripting (XSS) attacks.
XSS occurs when attackers inject malicious scripts into your app, which then execute in users browsers. This can lead to:
- Stolen session cookies
- Hijacked user accounts
- Defaced websites
Good news: React has built-in protections, but you need to reinforce them. In this guide, you'll learn:
- How XSS works in React
- React's built-in XSS defenses
- Proactive prevention techniques
- Code examples for secure apps
How XSS Works in React
Common XSS Attack Vectors
Dangerously Set Inner HTML
If you render unsanitized HTML, embedded scripts run:
// Vulnerable code
function UserBio({ bio }) {
return <div dangerouslySetInnerHTML={{ __html: bio }} />;
}
// If `bio` contains <script>maliciousCode()</script>, it executes!
URL Injection
// Vulnerable code
<a href="javascript:alert('Hacked!')">Click Me</a>
Third-Party Library Risks
Some npm packages may contain unsafe rendering.
React's Built-in XSS Protections
Automatic Escaping in JSX
React escapes all variables rendered in {}
by default:
// Safe: User input is treated as text, not HTML
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
// If `name` is "<script>alert(1)</script>", it won't execute!
}
No innerHTML Equivalent by Default
React requires explicit use of dangerouslySetInnerHTML
to render raw HTML, reminding developers of risks.
Proactive XSS Prevention in React
Sanitize Dynamic Content
Use DOMPurify to clean HTML before rendering:
import DOMPurify from 'dompurify';
function SafeHTML({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
Avoid dangerouslySetInnerHTML When Possible
Prefer React components over raw HTML:
// β Risky
<div dangerouslySetInnerHTML={{ __html: userContent }} />
// β
Safer (if HTML isn't needed)
<div>{userContent}</div>
Secure URL Handling
Sanitize dynamic URLs to prevent javascript: attacks:
function SafeLink({ url, children }) {
const isSafe = url.startsWith('http://') || url.startsWith('https://');
return <a href={isSafe ? url : '#'}>{children}</a>;
}
Use HTTP-Only Cookies for Sessions
Prevent cookie theft via XSS:
// Back-end should set cookies with:
Set-Cookie: sessionId=123; HttpOnly; Secure
Implement CSP (Content Security Policy)
Add a CSP header to block inline scripts:
Content-Security-Policy: script-src 'self' https://trusted-cdn.com;
Advanced Protections
Use Trusted Libraries for Markdown/Rich Text
Instead of rendering raw HTML, use:
- react-markdown (for Markdown)
- sanitize-html (for custom HTML)
Escape Data in HTTP Responses
Ensure your back-end API escapes JSON responses:
// Node.js example (using `he` library)
import he from 'he';
res.json({ bio: he.encode(user.bio) });
Real-World React XSS Scenarios
Scenario 1: Rendering User-Generated Content
import DOMPurify from 'dompurify';
function UserComment({ comment }) {
return (
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(comment),
}}
/>
);
}
Scenario 2: Dynamic Styling (Avoid eval-like Risks)
// β Unsafe (eval-like behavior)
<div style={{ background: userInput }} />;
// β
Safer (validate input first)
const isValidColor = color => /^#[0-9A-F]{6}$/i.test(color);
<div style={{ background: isValidColor(userInput) ? userInput : '#fff' }} />;
Key Takeaways
Do | Don't |
---|---|
Use DOMPurify for HTML | Trust raw innerHTML |
Escape dynamic URLs | Allow javascript: links |
Prefer text over HTML rendering | Use eval() or new Function() |
Enable CSP headers | Ignore third-party library risks |
Conclusion
React reduces XSS risks but doesn't eliminate them. To build truly secure apps:
- Sanitize all dynamic content
- Avoid dangerouslySetInnerHTML unless necessary
- Use CSP and HttpOnly cookies
By following these practices, you'll shield your React apps from XSS attacks. π‘οΈ