How to Prevent SQL Injection, XSS, and CSRF in Modern Web Apps
Security is one of those things developers know is important…
But somehow, it always becomes:
“I’ll handle it later.”
And then “later” turns into:
A vulnerability report
A broken app
Or worst… compromised user data
I’ve made this mistake. More than once.
What’s frustrating is that most security issues are not even “advanced hacking.”
They’re basic mistakes.
So let’s talk about the three most common ones:
SQL Injection
XSS (Cross-Site Scripting)
CSRF (Cross-Site Request Forgery)
And more importantly:
👉 How to actually prevent them in real apps.
🧩 Why These Attacks Still Exist
Because modern frameworks make us feel safe.
ORMs hide SQL
React escapes HTML
Auth libraries handle sessions
And we start thinking:
“I don’t need to worry about security.”
That’s where things go wrong.
💣 SQL Injection: The “I Trusted User Input” Mistake
What it is
SQL Injection happens when user input is directly inserted into a query.
❌ Bad Example
const query = `SELECT * FROM users WHERE email = '${email}'`;
const result = await db.query(query);If someone enters:
' OR 1=1 --
Your query becomes:
SELECT * FROM users WHERE email = '' OR 1=1 --👉 Congrats. You just exposed your entire database.
😤 The frustrating part
You might think:
“I would never do this.”
But in complex apps, raw queries sneak in.
✅ How to Prevent SQL Injection
1. Use Parameterized Queries
const result = await db.query(
"SELECT * FROM users WHERE email = ?",
[email]
);2. Use ORM Safely
With Prisma / Drizzle:
await db.user.findUnique({
where: { email },
});👉 No string concatenation = safe
3. Avoid Raw Queries (Unless Necessary)
If you must use them:
Always sanitize inputs
Use bindings
🧠 Real takeaway
Never trust user input. Ever.
🧨 XSS (Cross-Site Scripting): The “User Controls Your UI” Problem
What it is
XSS happens when attackers inject JavaScript into your app.
❌ Bad Example
<div dangerouslySetInnerHTML={{ __html: userInput }} />If user input is:
<script>alert('Hacked')</script>👉 That script runs in your app.
😤 The frustrating part
Sometimes you need to render HTML (e.g., rich text editors).
And that’s where things get risky.
✅ How to Prevent XSS
1. Avoid dangerouslySetInnerHTML
If possible:
👉 Don’t use it.
2. Sanitize HTML
import DOMPurify from "dompurify";
const clean = DOMPurify.sanitize(userInput);3. Use React (Properly)
React escapes content by default:
<div>{userInput}</div>👉 Safe
4. Set Security Headers
// example (Next.js headers)
{
"Content-Security-Policy": "default-src 'self'; script-src 'self'"
}🧠 Real takeaway
If users can inject HTML, they can inject JavaScript.
🔐 CSRF (Cross-Site Request Forgery): The “User Didn’t Mean That” Attack
What it is
CSRF tricks a user into making unintended requests.
Example
User is logged into your app.
They visit a malicious site that sends:
<form action="https://yourapp.com/api/delete-account" method="POST">
<input type="submit" />
</form>👉 If your app doesn’t protect against CSRF, the request succeeds.
😤 The frustrating part
You don’t see this happening.
It happens silently in the background.
✅ How to Prevent CSRF
1. Use CSRF Tokens
// backend
const token = generateCSRFToken();
// frontend
fetch("/api/action", {
method: "POST",
headers: {
"X-CSRF-Token": token,
},
});2. Use SameSite Cookies
Set-Cookie: session=abc123; SameSite=Strict;👉 Prevents cross-site requests
3. Use Auth Libraries
Libraries like NextAuth / Better Auth:
👉 Handle CSRF for you (mostly)
🧠 Real takeaway
If your app uses cookies for auth, you MUST think about CSRF.
⚖️ Modern Frameworks Help… But Don’t Save You
Let’s be honest:
ORMs reduce SQL injection risk
React reduces XSS risk
Auth libraries reduce CSRF risk
But none of them guarantee safety.
😤 The Dangerous Mindset
“The framework will handle it.”
That mindset creates vulnerabilities.
🧠 Practical Security Checklist
If you’re building a real app, do this:
✅ Database
Use parameterized queries
Avoid raw SQL
✅ Frontend
Never trust user input
Sanitize HTML
Avoid dangerous rendering
✅ Backend
Validate all inputs
Use schemas (Zod, etc.)
✅ Auth & Requests
Use CSRF tokens
Set secure cookies
Enable HTTPS
😤 The Truth Nobody Tells You
Here’s the uncomfortable truth:
Most security bugs are not complex.
They happen because:
You were in a hurry
You trusted input
You assumed something was safe
🧠 My Personal Lesson
After dealing with these issues:
I stopped trusting “default safety”
I started validating everything
I assume every input is malicious
Because honestly:
It’s easier to be paranoid than to fix a hacked system.
🧠 Final Thought
Security is not a feature.
It’s a mindset.
You don’t “add” it later.
You build with it from the beginning.
