← Back to the Build Your Homepage series
πŸ”’
EPISODE 03
Prepared statements Β· ORM Β· input validation

SQL Injection

SQL injection is decades old and still happening. Always use prepared statements, validate at boundaries, and treat ORM queries with the same care.

SQL injectionprepared statementsORMvalidation
Duration
⏱ About 1.5 hours
Level
πŸ“Š Intermediate
Prerequisite
🎯 db-02
OUTCOME
Write database queries that are impossible to inject

What you'll learn

  • 1Recognize a vulnerable query in 5 seconds
  • 2Always use parameterized queries
  • 3Validate input at the API boundary
  • 4Use ORMs safely (no raw template strings)

1. The Classic Example

javascript
// VULNERABLE β€” string concatenation
const sql = `SELECT * FROM users WHERE email = '${email}'`;
// email = "x' OR '1'='1" β€” returns ALL rows
// email = "x'; DROP TABLE users; --" β€” drops the table

2. Prepared Statements (Always)

javascript
// SAFE β€” placeholder + parameter
const stmt = db.prepare("SELECT * FROM users WHERE email = ?");
const user = stmt.get(email);

// PostgreSQL with pg
await pool.query("SELECT * FROM users WHERE email = $1", [email]);

// MySQL with mysql2
await pool.execute("SELECT * FROM users WHERE email = ?", [email]);
⚠️

Parameters are not string substitution β€” the driver sends them as separate data, so they can never be SQL.

3. ORMs

javascript
// Prisma β€” safe by design
await prisma.user.findUnique({ where: { email } });

// Raw query in Prisma β€” STILL safe with $queryRaw + tagged template
await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;

// DANGEROUS β€” string interpolation defeats the protection
await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`);

4. Defense in Depth

  • Validate input shape (Zod, Joi) β€” reject obvious garbage at the boundary
  • Use least-privileged DB users (the app user should not be able to DROP tables)
  • Log and rate-limit suspicious queries
  • Run a SAST scanner in CI (e.g., Semgrep)
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—