Step-by-Step Guide
6 steps

How to Prevent SQL Injection

SQL injection allows attackers to execute arbitrary SQL commands on your database. It can lead to data theft, data modification, or complete database destruction. This guide covers how to prevent SQL injection using parameterized queries, ORMs, and proper input validation.

Find security issues automatically before attackers do.

Follow These Steps

1

Never use string concatenation for SQL queries

The root cause of SQL injection is inserting user input directly into SQL strings.

Code Example
// VULNERABLE - string concatenation
const query = `SELECT * FROM users WHERE email = '${email}'`
// Attacker input: ' OR 1=1 --
// Result: SELECT * FROM users WHERE email = '' OR 1=1 --'

// VULNERABLE - template literals
const query = `SELECT * FROM users WHERE id = ${id}`
// Attacker input: 1; DROP TABLE users;
// Result: SELECT * FROM users WHERE id = 1; DROP TABLE users;
2

Use parameterized queries

Parameterized queries separate SQL code from data, making injection impossible.

Code Example
// Node.js with pg
const result = await pool.query(
  'SELECT * FROM users WHERE email = $1',
  [email]
)

// Node.js with mysql2
const [rows] = await connection.execute(
  'SELECT * FROM users WHERE email = ?',
  [email]
)

// Python with psycopg2
cursor.execute(
  "SELECT * FROM users WHERE email = %s",
  (email,)
)
3

Use an ORM for type-safe queries

ORMs like Prisma, Drizzle, and SQLAlchemy generate parameterized queries automatically.

Code Example
// Prisma - always parameterized
const user = await prisma.user.findUnique({
  where: { email: email }
})

// Drizzle - always parameterized
const user = await db.select().from(users).where(eq(users.email, email))

// CAUTION: Raw queries in ORMs can still be vulnerable
// Prisma raw query - still safe with template
const users = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`

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

Validate input types and formats

Validate that input matches expected types before using in queries.

Code Example
import { z } from 'zod'

const SearchParams = z.object({
  id: z.coerce.number().int().positive(),
  email: z.string().email().max(255),
  status: z.enum(['active', 'inactive', 'pending'])
})

// Only query with validated data
const params = SearchParams.parse(req.query)
const result = await pool.query(
  'SELECT * FROM users WHERE id = $1 AND status = $2',
  [params.id, params.status]
)
5

Use least-privilege database users

Your application database user should only have the permissions it needs.

Code Example
-- Create a restricted database user
CREATE USER app_user WITH PASSWORD 'strong-password';

-- Grant only necessary permissions
GRANT SELECT, INSERT, UPDATE ON users TO app_user;
GRANT SELECT, INSERT ON posts TO app_user;

-- Never use the superuser account in your application
-- REVOKE DROP, CREATE, ALTER from the app user
6

Test for SQL injection vulnerabilities

Use automated tools and manual testing to find injection points.

Code Example
# Manual testing payloads (use in test environment only)
# Text fields: ' OR 1=1 --
# Numeric fields: 1 OR 1=1
# Search fields: %; DROP TABLE users; --

# Run VAS scan to detect SQL injection vulnerabilities automatically

What You'll Achieve

All database queries use parameterized queries or an ORM. Input is validated before querying. The database user has minimal permissions. Your application is protected against SQL injection.

Common Mistakes to Avoid

Mistake

Using $queryRawUnsafe or equivalent methods

Fix

Always use parameterized raw queries. In Prisma, use $queryRaw with template literals, not $queryRawUnsafe with string concatenation.

Mistake

Thinking ORMs are automatically safe

Fix

ORMs are safe for their standard query methods, but raw query methods can still be vulnerable. Always use parameterized syntax for raw queries.

Mistake

Validating input as the only defense

Fix

Input validation is defense-in-depth but not the primary defense. Parameterized queries make injection impossible regardless of input.

Frequently Asked Questions

Are ORMs immune to SQL injection?

Standard ORM methods (findUnique, select, where) are safe. But raw query methods like $queryRawUnsafe in Prisma or raw() in some ORMs can be vulnerable if you use string concatenation.

Does input validation prevent SQL injection?

It reduces attack surface but is not sufficient alone. Parameterized queries are the primary defense because they make injection structurally impossible, regardless of input content.

Can NoSQL databases get SQL injection?

Not SQL injection specifically, but NoSQL injection exists. MongoDB, for example, is vulnerable to operator injection if user input is passed directly to query operators. Always validate input.

Ready to Secure Your App?

VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.

Start Security Scan