Why SQLi still matters for React apps
React runs in the browser, but your API and database live on the server—that’s where SQL injection (SQLi) happens. Your job in React is to validate, constrain, and normalize inputs before they ever touch SQL on the backend. Then, on the server, use parameterized queries only.
Quick win: Scan your site or staging URL with our free Website Vulnerability Scanner to catch injection risks early.
1) Never build SQL strings (server)
Bad (vulnerable):
// Node/Express (DO NOT DO THIS)
const q = `SELECT * FROM users WHERE email='${req.body.email}'`;
const rows = await db.query(q);
Good (parameterized):
// Using node-postgres
const rows = await pg.query(
'SELECT * FROM users WHERE email=$1',
[req.body.email]
);
2) Validate at the React boundary
Use a schema to type-check and normalize before sending to the API.
// React + zod
import { z } from "zod";
const LoginSchema = z.object({
email: z.string().email().max(254),
pass: z.string().min(8).max(128)
});
// Send normalized JSON, not ad-hoc querystrings
const onSubmit = async (form: any) => {
const data = LoginSchema.parse(form);
await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
};
Screenshot of the homepage of our Website Vulnerability Scanner
Screenshot of the free tools webpage where you can access security assessment tools.
3) Constrain dynamic parts with allowlists
Attackers love fields like sort
, order
, limit
.
// Server
const SORTS = { name: "name", created: "created_at" };
const dir = req.body.dir === "desc" ? "DESC" : "ASC";
const sort = SORTS[req.body.sort] ?? SORTS.name;
await pg.query(
`SELECT id,name FROM projects ORDER BY ${sort} ${dir} LIMIT $1`,
[ Math.min(Number(req.body.limit) || 25, 100) ]
);
4) Keep SQL in params—even with ORMs
ORMs help, but you can still shoot your foot if you interpolate.
// Bad with Prisma (string concat)
await prisma.$queryRawUnsafe(
`SELECT * FROM posts WHERE title LIKE '%${term}%'`
);
// Good with Prisma (params)
await prisma.$queryRaw`SELECT * FROM posts WHERE title LIKE ${'%' + term + '%'}`;
5) Don’t trust HTML sinks in React
Avoid reflecting raw user strings into the DOM.
// Prefer plain text rendering; avoid dangerouslySetInnerHTML
<p>{userInput}</p>
End-to-end example (React + Express + pg)
React form → safe API → parameterized SQL
// React
const create = async (name: string) => {
await fetch("/api/projects", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name })
});
};
// Express route (server)
app.post("/api/projects", async (req,res) => {
const { name } = req.body; // validated earlier
const r = await pg.query(
"INSERT INTO projects(name) VALUES($1) RETURNING id,name",
[name]
);
res.json(r.rows[0]);
});
Sample Assessment Report by our free tool to check Website Vulnerability
Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.
Keep scanning while you build
Run your site through our free checker regularly for a Website Security test.
Also, explore more practical guides on our blog on Pentest Testing Corp.
Managed IT Services (New)
Stabilize infra, patch fast, and harden configs with our Managed IT Services:
https://www.pentesttesting.com/managed-it-services/
AI Application Cybersecurity (New)
Shipping AI? Secure prompts, model calls, and data pipelines:
https://www.pentesttesting.com/ai-application-cybersecurity/
Offer Cybersecurity to Your Clients (New)
Agencies: white-label our pentesting & security program:
https://www.pentesttesting.com/offer-cybersecurity-service-to-your-client/
Get updates
Subscribe on LinkedIn https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7327563980778995713
Pro tip: Combine strict React validation, minimal API surface, and parameterized queries everywhere. That trio kills most SQLi paths before they start.
Top comments (0)