6 min read

SQL injection prevention in Node.js — parametrize everything

SQL injection is a solved problem — and AI coding assistants keep re-introducing it. Here are the exact patterns to watch for in Prisma, Supabase, Drizzle, and raw pg.

Every ORM supports parameterized queries. AI-generated code sometimes reaches for raw SQL anyway — to ship faster, to match a copy-pasted example. This guide covers the exact patterns that slip through code review.

What it is

SQL injection happens when user input is interpolated into a SQL string. The fix is parameter binding, where the database receives SQL and values as separate inputs and never compiles user input as SQL.

Vulnerable example

// Vulnerable in every ORM:
// 1. Raw pg
const result = await pool.query(`SELECT * FROM users WHERE email = '${email}'`);

// 2. Prisma raw query
const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`);

// 3. Drizzle sql template with interpolation
const users = await db.execute(sql`${sql.raw(`SELECT * FROM users WHERE email = '${email}'`)}`);

Fixed example

// Fixed: parameter binding
// 1. Raw pg
const result = await pool.query("SELECT * FROM users WHERE email = $1", [email]);

// 2. Prisma typed
const users = await prisma.user.findMany({ where: { email } });

// 3. Prisma raw with bindings
const users = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;

// 4. Drizzle typed
const users = await db.select().from(usersTable).where(eq(usersTable.email, email));

How Securie catches it

Securie's taint analyzer traces every user input into every SQL sink across the most common Node ORMs, flagging any string interpolation that reaches SQL.

Checklist

  • Zero string concatenation in SQL
  • Raw query APIs (`$queryRawUnsafe`, `pool.query` with template strings) are grep'd for and each use is reviewed
  • LIKE clauses use parameterized %s with explicit escape
  • Dynamic identifiers (table/column names from user input) are whitelisted, not concatenated
  • Database users have read-only where possible; prod DB cannot DROP

FAQ

What about NoSQL injection?

Same principle. MongoDB drivers bind parameters correctly; `$where` with interpolation is the equivalent anti-pattern. Securie covers MongoDB too.