What is SQL Injection?
Definition
SQL injection is a vulnerability that arises when an application pastes user-supplied data directly into the text of an SQL query, letting that data change what the query means. It can lead to data leaks, modified or deleted records and bypassed authentication. The primary defence is using parameterized queries, also called prepared statements, which send values to the database separately from the query text.
Also known as: SQLi, SQL injection attack, SQL injection vulnerability, SQL code injection

When data is treated as code
An SQL query is code that the database executes. When an application concatenates a user-supplied value into that code as plain text, the database can no longer tell which part the developer wrote and which part came from the user. If the value contains characters that mean something in SQL, such as a quote, the structure of the query itself can change. Every variant of SQL injection comes from this one cause: data and code travelling through the same channel.
A vulnerable query and a safe one
// Vulnerable: the value is pasted into the query text
const result = await db.query(
"SELECT id, total FROM orders WHERE customer_email = '" + email + "'"
);
// Safe: the value is sent separately as a parameter
const result = await db.query(
"SELECT id, total FROM orders WHERE customer_email = $1",
[email]
);In the second version the database parses the query's structure first and only then slots the value in, strictly as data. Whatever email contains, it cannot alter the query; at worst it is an odd email address that matches nothing. Placeholder syntax varies by driver ($1, ?, :email) but the principle is the same.
What is at stake
A successful injection can hand an attacker anything the application's database account is allowed to do: reading customer records and password hashes from other tables, changing prices or permissions, deleting data and bypassing the login query entirely. Even when no results appear on screen, "blind" variants can leak data through differences in timing or behaviour, so hiding error messages does not close the hole. Injection sits in the OWASP Top 10 under its Injection category, a long-known vulnerability class that still turns up in audits.
Where parameters do not reach
Table names, column names and the direction of an ORDER BY cannot be bound as parameters. In those cases user input should never enter the query at all; map it to a fixed allowlist defined in code:
const sortColumns = { date: "created_at", total: "order_total" };
const column = sortColumns[req.sort] ?? "created_at";
const direction = req.dir === "asc" ? "ASC" : "DESC";An ORM generates parameterized queries by default, but feeding its raw-SQL helpers with string concatenation brings the risk straight back. Stored procedures that build dynamic SQL internally are no safer. Escaping input by hand is fragile, and OWASP's SQL injection prevention cheat sheet strongly discourages relying on it.
Layers that limit the damage
- Least privilege: the application's database user should not be able to drop tables or alter the schema, and should only touch the tables it needs. The principle of least privilege caps the blast radius of a flaw.
- Quiet errors: raw database errors belong in logs, not in responses.
- Input validation: checking that an ID is a number and a date is a date narrows the attack surface, but never replaces parameterization.
- WAFs and scanners: they can catch known patterns; they complement fixed code rather than substituting for it.

