Databases in practice
Use parameterized queries
Keep SQL syntax separate from untrusted values so submitted data cannot become executable query text.
8 minute lesson
Never build a query by concatenating user input into the SQL string.
This is unsafe because submittedEmail becomes part of the SQL program:
const sql = `SELECT id, email FROM users WHERE email = '${submittedEmail}'`
const result = await db.query(sql)
Use a placeholder and pass the value separately:
const result = await db.query(
'SELECT id, email FROM users WHERE email = $1',
[submittedEmail]
)
The database receives the SQL structure and the value through separate channels. A value such as ' OR 1=1 -- remains a string to compare with email; it cannot add an OR condition to the query.
Placeholder syntax depends on the driver. PostgreSQL drivers commonly use $1, while other drivers use ? or named placeholders. Use the parameter API documented by your driver rather than trying to escape input yourself.
Parameters represent values. They usually cannot stand for table names, column names, keywords, or sort directions. If a user can choose a sort order, map a small allow-listed value to SQL you control:
const orderBy = submittedSort === 'oldest'
? 'created_at ASC'
: 'created_at DESC'
Parameterized queries prevent SQL injection through values. They do not replace input validation or authorization: a safely encoded user ID is still dangerous if the signed-in user is not allowed to access it.
Your action: pass a quote-heavy string such as ' OR 1=1 -- through a parameterized lookup. Verify that it is treated as one email value and that no unrelated rows are returned.
Lesson completed