What is SQL injection, and how do you actually prevent it?
An attack where user input is interpreted as SQL code rather than as data — and it happens for one reason: the query was built by concatenating strings, so the database cannot tell your instructions apart from the attacker's.
The mechanism. If a query is assembled as "SELECT * FROM users WHERE email = '" + input + "'", an input containing a quote character ends the string early and everything after it becomes part of the command. The classic payload closes the quote and appends a condition that is always true, or terminates the statement and starts another.
What it enables: reading data the user should not see, modifying or deleting records, bypassing authentication entirely, and in some configurations reading files or executing commands on the host.
The one real fix: parameterised queries (prepared statements). The query structure is sent to the database separately from the values. The database parses the statement first, then binds the parameters as data — so a quote character in the input is just a quote character, and there is no path by which it becomes syntax. This is not escaping done well; it is a different mechanism, and that is why it works.
What does not reliably work:
Manual escaping. It depends on character set, database and context, and a single missed case is a vulnerability. Every attempt to do this by hand has a history of bypasses.
Blocklisting keywords, which is trivially evaded by encoding and case variation.
Client-side validation, which an attacker simply skips.
Stored procedures, which help only if they do not themselves concatenate.
What you must handle carefully even with parameters. Table and column names, and ORDER BY clauses, cannot be parameterised — they are identifiers, not values. Where these must be dynamic, validate against an allowlist of known-good names rather than interpolating input.
Defence in depth: least-privilege database accounts, so a successful injection reaches as little as possible; input validation as a secondary control; disabling detailed error messages in production; and a web application firewall as a mitigation, never as the fix.
ORMs help by default and reintroduce the risk the moment you drop to raw SQL.