We use analytics cookies.We use cookies to analyze site traffic and improve your experience.
Loading...
System Design Masterclass · Security Architecture
Free preview: the first 2 parts of this 4-part lesson.
Already bought the course? Sign in to read the whole lesson.
this lesson is part of
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design courseread the rest of this lesson in
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design coursein Security Architecture
This lesson is part of the full System Design Masterclass. Unlock it along with 769 other lessons for a one-time payment.
One-time payment by card or PayPal
Get Lifetime AccessOne-time payment: UPI, card or netbanking
Get Lifetime Access18 Foundation lessons are free, no payment needed. Try them now
It is easy and good
“It is really nice and good Please join the course”
Parameterized queries are the single most effective defense against injection. They do not just make injection harder. They make it architecturally impossible.
// VULNERABLE (string concatenation):
const query = `SELECT * FROM users WHERE email = '${email}'`;
// If email is: ' OR '1'='1' --
// Result: SELECT * FROM users WHERE email = '' OR '1'='1' --'
// Returns ALL users. Database is breached.
// SAFE (parameterized):
const query = 'SELECT * FROM users WHERE email = $1';
const result = await db.query(query, [email]);
// If email is: ' OR '1'='1' --
// Database searches for a user with the literal email: "' OR '1'='1' --"
// Finds nothing. Because nobody has that email. Injection failed.
The difference is not just syntax. It is structural. With string concatenation, the database receives one blob of text and must parse the entire thing as SQL. It cannot tell where your code ends and the attacker's code begins. With parameterized queries, the database receives the SQL template and the values as separate inputs. The template is compiled into an execution plan first. Then the values are bound. Since compilation already happened, the values can never change the query's structure. Not "difficult to change." Impossible to change.
Every major database supports this. PostgreSQL, MySQL, SQL Server, Oracle, SQLite. Every language has a parameterized query API. There is no technical excuse for using string concatenation with user input in SQL.
The syntax varies by language and driver, but the pattern is identical: template with placeholders, values in a separate array.
| Language/ORM | Parameterized Syntax | Placeholder style |
|---|---|---|
| Node.js (pg) | query('SELECT * FROM users WHERE id = $1', [id]) | $1, $2, $3 |
| Python (psycopg2) | cursor.execute('SELECT * FROM users WHERE id = %s', (id,)) | %s |
| Java (JDBC) | PreparedStatement.setString(1, id) | ? |
| Go (database/sql) | db.Query("SELECT * FROM users WHERE id = $1", id) | $1 |
| Drizzle ORM | db.select().from(users).where(eq(users.id, id)) | Generated |
| Prisma | prisma.user.findUnique({ where: { id } }) | Generated |
ORMs parameterize by default. Drizzle, Prisma, SQLAlchemy, ActiveRecord, and Entity Framework all generate parameterized queries. You never write SQL. The queries they produce are safe.
The danger is the escape hatch. When developers bypass the ORM for "complex" queries, they sometimes fall back to string concatenation. In Drizzle, the sql`SELECT * FROM users WHERE id = ${id}` tagged template parameterizes correctly. But db.execute('SELECT... ' + id) does not. In Prisma, $queryRaw is safe. $queryRawUnsafe is not. The function names are designed to make you think twice, but developers under time pressure skip the thinking.
Search your codebase for string concatenation near SQL keywords. If you find any, that code is a SQL injection vulnerability. Fix it before an attacker finds it.