Skip to main content

Parameterised queries & SQL injection

Summary: Never build SQL by concatenating user input into the query string — an attacker can smuggle SQL through a value. Use parameterised queries: pass a placeholder in the SQL and the values separately, and the driver escapes them safely. The placeholder syntax differs by language; the rule is universal.

Two ways to pass values

Option A — string concatenation (dangerous):

conn.execute(f"SELECT * FROM users WHERE name = '{name}'")

Option B — parameterised query (safe):

conn.execute("SELECT * FROM users WHERE name = %s", (name,))

In option B, %s is a placeholder. You pass the actual value separately, and the database driver substitutes it safely — escaping any special characters before it ever reaches the SQL engine.

★ SQL injection

SQL injection is when an attacker puts SQL code inside a value (a form field, a URL param) and the app accidentally runs it as a real SQL command.

Login form — auth bypass:

# Vulnerable
username = "admin' OR '1'='1"
conn.execute(f"SELECT * FROM users WHERE username = '{username}'")

The query becomes:

SELECT * FROM users WHERE username = 'admin' OR '1'='1'

'1'='1' is always true → returns every row → the attacker is logged in as anyone.

Data destruction:

sc_id = "SC-149949'; DROP TABLE patch_sessions; --"
conn.execute(f"SELECT * FROM patch_sessions WHERE sc_id = '{sc_id}'")

The query becomes:

SELECT * FROM patch_sessions WHERE sc_id = 'SC-149949'; DROP TABLE patch_sessions; --'

The entire patch_sessions table is gone.

Why parameterised queries stop this: the driver escapes the value (a single quote ' becomes the literal ''), so the injected SQL is treated as plain text data, never as a command.

Placeholder syntax varies — the concept is universal

Language / driverPlaceholder
Python / psycopg%s
JavaScript / node-postgres$1, $2, $3
C# / ADO.NET@sc_id
Java / JDBC?

The rule

Never concatenate user input directly into SQL strings. Always parameterise.