🛡️ React Security: Keeping Your App Safe from Sneaky Code
The Story of the Mischievous Messenger
Imagine you have a magical mailbox in your house. Friends can send you letters, and whatever is written in those letters appears on your wall automatically.
One day, a sneaky trickster sends you a letter that says:
“Draw a big ugly monster on the wall and break all the windows!”
Because your mailbox blindly follows instructions, your house gets ruined! 😱
This is exactly what XSS (Cross-Site Scripting) does to websites. Bad actors send harmful code, and if your app doesn’t check it first, the code runs and causes chaos.
🎯 What You’ll Learn
- XSS Prevention - How to block sneaky code
- dangerouslySetInnerHTML - The “handle with care” box
- Input Sanitization - Cleaning dirty messages
1️⃣ XSS Prevention: The Security Guard
What is XSS?
XSS stands for Cross-Site Scripting. It’s when bad people inject harmful code (usually JavaScript) into your website.
Think of it like this:
- Your website is a birthday party 🎉
- Everyone writes messages on a big board
- A troublemaker writes: “Run around and break everything!”
- If people follow that instruction, party ruined!
How React Protects You (Automatically!)
Good news! React is like a smart security guard. By default, it treats everything as plain text.
function Comment({ userInput }) {
// React escapes this automatically!
// Even if userInput contains <script>
// it shows as text, not code
return <div>{userInput}</div>;
}
What happens:
- User types:
<script>alert('hacked!')</script> - React shows:
<script>alert('hacked!')</script>(as text) - The code does NOT run!
The Escape Magic ✨
React converts dangerous characters into safe ones:
| Dangerous | Safe Version |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
& |
& |
This is called escaping. It’s like translating “attack words” into harmless symbols.
2️⃣ dangerouslySetInnerHTML: The “Handle With Care” Box
When You MUST Show Real HTML
Sometimes you need to display actual HTML - like content from a rich text editor.
React provides a special tool: dangerouslySetInnerHTML
The name is intentionally scary because it’s dangerous!
function Article({ content }) {
// ⚠️ DANGEROUS - Only use with
// trusted, sanitized content!
return (
<div
dangerouslySetInnerHTML={{
__html: content
}}
/>
);
}
Why Is It Dangerous?
graph TD A["User Input"] --> B{Sanitized?} B -->|No| C["💀 XSS Attack!"] B -->|Yes| D["✅ Safe Display"] C --> E["Stolen cookies"] C --> F["Fake login forms"] C --> G["Hijacked sessions"]
The Golden Rules
- NEVER use it with user input directly
- ALWAYS sanitize first (we’ll learn how!)
- Ask yourself: “Do I really need this?”
Example: Right vs Wrong
// ❌ WRONG - Direct user input!
<div
dangerouslySetInnerHTML={{
__html: userComment
}}
/>
// ✅ RIGHT - Sanitized first!
import DOMPurify from 'dompurify';
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(userComment)
}}
/>
3️⃣ Input Sanitization: The Cleaning Machine
What Is Sanitization?
Think of it as a car wash for data:
- Dirty car (user input) goes in
- Clean car (safe content) comes out
- All the mud (malicious code) is removed!
Meet Your Best Friend: DOMPurify
DOMPurify is a library that cleans HTML for you.
npm install dompurify
How to Use It
import DOMPurify from 'dompurify';
function SafeContent({ htmlString }) {
const cleanHTML = DOMPurify.sanitize(
htmlString
);
return (
<div
dangerouslySetInnerHTML={{
__html: cleanHTML
}}
/>
);
}
What DOMPurify Removes
| Input | Output |
|---|---|
<script>bad()</script> |
(removed) |
<img onerror="bad()"> |
<img> |
<a onclick="bad()"> |
<a> |
<b>Hello</b> |
<b>Hello</b> ✅ |
Custom Configuration
You can control what’s allowed:
// Only allow specific tags
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong'],
ALLOWED_ATTR: ['class']
});
🧪 Real-World Example: Comment System
Let’s build a safe comment section!
import { useState } from 'react';
import DOMPurify from 'dompurify';
function CommentBox() {
const [input, setInput] = useState('');
const [comments, setComments] = useState([]);
const addComment = () => {
// Sanitize before storing!
const safe = DOMPurify.sanitize(input);
setComments([...comments, safe]);
setInput('');
};
return (
<div>
<textarea
value={input}
onChange={(e) => setInput(
e.target.value
)}
placeholder="Write a comment..."
/>
<button onClick={addComment}>
Post
</button>
{comments.map((c, i) => (
<div
key={i}
dangerouslySetInnerHTML={{
__html: c
}}
/>
))}
</div>
);
}
🎯 Quick Decision Tree
graph TD A["Need to display content?"] --> B{Is it user input?} B -->|No, static| C["Use normal JSX"] B -->|Yes| D{Need HTML formatting?} D -->|No| E["Use normal JSX<br/>React escapes it!"] D -->|Yes| F["Sanitize with DOMPurify"] F --> G["Use dangerouslySetInnerHTML"]
✅ Security Checklist
Before shipping your app:
- [ ] Never trust user input directly
- [ ] Use DOMPurify when HTML is needed
- [ ] Avoid dangerouslySetInnerHTML when possible
- [ ] Keep dependencies updated
- [ ] Test with XSS attack strings
🚀 You Did It!
You now understand:
- XSS is when bad code sneaks into your site
- React auto-escapes content by default (yay!)
- dangerouslySetInnerHTML bypasses protection (use carefully!)
- DOMPurify cleans HTML before display
Remember the mailbox story: Always check the mail before putting it on your wall! 📬✨
📚 Key Terms
| Term | Simple Meaning |
|---|---|
| XSS | Bad code injection attack |
| Escaping | Converting < to < etc. |
| Sanitization | Removing dangerous code |
| DOMPurify | Library that cleans HTML |
