Parameterized queries keep SQL code and data separate, preventing injection
SQL injection happens when user input is concatenated into a SQL string, so the database cannot tell where the query ends and the data begins. An attacker can then change the structure of the query, bypass authentication, read or modify data, or execute administrative commands. Parameterized queries fix this by sending the SQL with placeholders and the values separately. The database driver parses the statement once with the placeholders, then binds the values as data, so metacharacters in the input cannot change the query structure. In Python, every major driver supports this: sqlite3 uses ? placeholders, psycopg uses %s, and most ORMs support it under the hood. The rule is to never build SQL with string formatting, f-strings, or concatenation for values. Table names, column names, and other identifiers cannot be parameterized, so they must be validated against an allowlist.
Use placeholders and pass parameters as a tuple or dict. The driver handles quoting and escaping.
Never use % formatting, f-strings, or .format() to build SQL with user input.
Identifiers (table names, column names) cannot be parameterized. Validate them against an allowlist of known names.
ORMs are not automatically safe: raw SQL and string-built fragments still introduce risk.
Stored procedures can also be vulnerable if they build dynamic SQL internally.
Common mistake: using parameterized queries for values but concatenating the table name from user input. That is still injectable.
Common mistake: assuming that escaping single quotes is enough. Attackers use encodings, comments, and stacked queries.
Version note: the DB-API parameter style differs per driver (qmark for sqlite3, format for psycopg). Check the driver documentation.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience