SQL injection: never glue user text into a query
The queries on this site are typed by you on purpose. In an application, some of the text in a query comes from a form, a URL or an API. If that text is pasted into the SQL, a visitor can change what the statement does. This page shows the mistake on the bakery database, then the fix. It is not a guide to attacking other systems.
The query you meant
Suppose an app looks up a customer by last name. The intended SQL is a filter on that name only.
SELECT first_name, last_name FROM customers WHERE last_name = 'Lopez' ORDER BY customer_id;
Result: Ana Lopez, one row.
What glued-in text can do
If the app builds the statement as `WHERE last_name = '` plus whatever the visitor typed plus `'`, then typing extra SQL inside that string changes the filter.
The query below is the same statement after someone types a last name that never ends the quote and adds `OR 1 = 1`. `1 = 1` is always true, so the WHERE no longer restricts rows. The same class of bug on UPDATE or DELETE without a tight WHERE can change or remove every row.
SELECT first_name, last_name FROM customers WHERE last_name = 'Lopez' OR 1 = 1 ORDER BY customer_id;
Result: All eight customers. The name filter no longer limits the result.
The fix: bound parameters
Do not join user text into SQL. Send the statement with a placeholder and pass the value separately. PostgreSQL uses `$1`, `$2`, and so on. The database treats the value as data, never as syntax, so a last name that looks like SQL stays a last name.
The query you write is then always `WHERE last_name = $1`. Libraries and ORMs call this a parameterized query or a prepared statement. You still have to use it: an ORM that concatenates strings has the same bug.
SELECT first_name, last_name FROM customers WHERE last_name = 'Lopez' ORDER BY customer_id;
Result: Ana Lopez again: the lookup with a bound value, shown here with a literal because this editor has no application layer.
Common questions
- Is quoting the value with extra apostrophes enough?
- No. People forget an escape, encodings differ, and numbers are not quoted at all. Bound parameters skip that whole class of mistakes.
- Does an ORM mean I can ignore this?
- Only if every query goes through its parameter API. Raw SQL strings inside an ORM are still concatenated SQL. Prefer the query builder or `execute(sql, [value])`.
- What about UPDATE and DELETE?
- The same rule: the WHERE must use parameters, never glued text. A missing or broken WHERE on those statements is how a whole table is changed at once.