SQL Injection
An attack where malicious SQL is inserted into a query through user input. Prevented by parameterized queries and prepared statements. Never concatenate user input into SQL.
What is SQL Injection?
In short
SQL injection is a web vulnerability where an attacker slips their own SQL into a database query by feeding crafted text into an input the application trusts, letting them read, change, or delete data they should never reach. It happens when user input is concatenated directly into a SQL string instead of being passed as a bound parameter, and it is fixed by using parameterized queries or prepared statements.
What it is
A database query is just text the application hands to a SQL engine like PostgreSQL or MySQL. SQL injection happens when part of that text comes from a user and the application glues it in raw. The attacker writes input that is not data but more SQL, so the engine runs commands the developer never intended.
The classic example is a login form. The app builds a query like SELECT * FROM users WHERE email = 'INPUT' AND password = 'INPUT'. If a user types ' OR '1'='1 into the email box, the query becomes WHERE email = '' OR '1'='1' AND ... which is always true, so the database returns every row and the attacker logs in as the first user, often an admin.
From there an attacker can read other tables (UNION SELECT to pull password hashes or credit card data), modify rows, drop tables, or in some setups run operating system commands. It is consistently one of the top entries in the OWASP Top 10 and has been behind major breaches for over two decades.
How it works under the hood
The root cause is that the database cannot tell the difference between the query the developer wrote and the data a user supplied once they are merged into one string. The parser sees a single block of SQL and trusts all of it. Quote characters, comment markers like --, and statement separators like the semicolon become weapons because they let the attacker break out of the intended data slot and into the command structure.
Parameterized queries (also called prepared statements) close this gap by sending the query template and the values over separate channels. The app sends SELECT * FROM users WHERE email = $1 with a placeholder, then sends the actual email value separately. The database compiles the query plan first, so the value can only ever be treated as data. Even ' OR '1'='1 just becomes a literal string the engine searches for, and the search simply finds no matching email.
Two related flavors matter in practice. Blind SQL injection is when the app does not show query results or errors, so the attacker infers data one bit at a time by watching whether a page changes (boolean blind) or how long it takes to respond (time blind, using commands like SLEEP). Second-order injection is when malicious input is stored safely once, then later concatenated into a different query that does not escape it.
How to prevent it and the trade-offs
The primary defense is parameterized queries everywhere user input touches SQL. Most languages and ORMs do this by default: in Python use psycopg with cursor.execute(query, params), in Node use the pg library with $1 placeholders, in Java use PreparedStatement. An ORM like Drizzle, Prisma, or Hibernate generates parameterized SQL for you, which is why ORMs cut down injection bugs, but the protection breaks the moment you drop to raw string SQL or build a query with string concatenation.
Input validation and allowlisting help as a second layer, especially for things that cannot be parameterized such as table names, column names, or ORDER BY directions. For those, check the value against a fixed list of permitted names rather than escaping it. Escaping input by hand is fragile and easy to get wrong across different databases, so it should be a last resort, not the main strategy.
Defense in depth limits the damage if injection still happens. Give the application database account only the privileges it needs (a read API user should not be able to DROP tables), put a web application firewall in front to catch obvious payloads, and turn off detailed SQL error messages in production so attackers cannot use them to map your schema. The cost of all this is mostly discipline, not performance, since prepared statements are usually as fast or faster because the database caches the compiled plan.
A concrete real-world example
In 2008 Heartland Payment Systems was breached through SQL injection on a web form, leading to the theft of more than 130 million credit card numbers, one of the largest card breaches of its era. The attackers used the injected access as a foothold to plant malware deeper in the network.
The 2011 Sony Pictures breach by LulzSec also started with a single unparameterized query. The group pulled over a million user accounts, and Sony had stored many passwords in plain text, which turned a query bug into a full credential dump. The lesson engineers took from it was that injection plus poor data hygiene multiply each other.
These were not exotic attacks. Both came down to a query that pasted user input straight into SQL. A single parameterized statement on the vulnerable form would have stopped the initial entry, which is why the rule never concatenate user input into SQL is treated as non-negotiable in code review today.
Where it is used in production
PostgreSQL
Supports server-side prepared statements with $1 placeholders, so client libraries like psycopg and node-postgres send query templates and values separately by default.
MongoDB
Document databases face the analogous NoSQL injection when query objects are built from raw user input; MongoDB drivers mitigate it by treating supplied values as data rather than operators.
Cloudflare
Its web application firewall ships managed rules that detect and block common SQL injection payloads in HTTP requests before they reach the origin server.
OWASP
Maintains the canonical SQL Injection Prevention Cheat Sheet and ranks injection in its Top 10 web risks, the reference most security teams build their standards on.
Frequently asked questions
- Do ORMs make my app immune to SQL injection?
- Mostly, but not automatically. ORMs like Prisma, Drizzle, and Hibernate generate parameterized queries for normal operations, which is safe. You reintroduce the risk the moment you use a raw query method and build the string with concatenation or template literals containing user input. Use the ORM's parameter binding even for raw queries.
- What is the difference between parameterized queries and escaping input?
- Parameterized queries send the query and the values separately, so the database never parses user input as SQL. Escaping tries to neutralize dangerous characters in a string you still concatenate, which is error-prone and differs across databases. Parameterized queries are the reliable fix; escaping is a fragile fallback only for cases that cannot be parameterized, like table names.
- Can SQL injection happen if I do not show query results to the user?
- Yes, this is blind SQL injection. The attacker cannot see results directly, so they ask true or false questions and watch the page behavior, or use time delays like SLEEP to read data one character at a time. It is slower but just as dangerous, so hiding output is not a defense.
- Is SQL injection still a real threat today?
- Yes. It still appears in the OWASP Top 10 and is found regularly in pentests, especially in legacy code, internal tools, and places where developers drop to raw SQL for performance or complex queries. The fix is well known, but it only works if applied everywhere user input meets the database.
- Why is limiting database account privileges important if I already use parameterized queries?
- It is defense in depth. If a single query somewhere is missed or a new vulnerability appears, a low-privilege account limits the blast radius. A read-only API user that cannot DROP tables or read other schemas turns a potential disaster into a contained incident.
Learn SQL Injection hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
Related lessons
Lessons that touch on SQL Injection as part of a larger topic.
Parameterized Queries
The complete defense against SQL injection, separating SQL structure from user data
intermediate · security architecture
Web Application Firewall (WAF)
Filtering malicious HTTP traffic before it reaches your application, blocking SQL injection, XSS, and other web attacks at the edge
intermediate · security architecture
See also
Related glossary terms you might want to look up next.
WAF
Web Application Firewall: filters and monitors HTTP traffic between a web application and the internet. Blocks SQL injection, XSS, and other OWASP top-10 attacks.
XSS
Cross-Site Scripting: an attack where malicious scripts are injected into trusted websites. Prevented by sanitizing user input and setting Content-Security-Policy headers.
SQL
Structured Query Language for managing relational databases. Tables, rows, columns, and powerful joins to query related data.
Throttling
Slowing down the rate of processing requests instead of rejecting them outright. The gentler cousin of rate limiting.
OAuth
An authorization framework that lets users grant third-party apps limited access to their accounts without sharing passwords. Powers 'Sign in with Google.'
JWT
JSON Web Token: a compact, self-contained token for transmitting claims between parties. The server can verify it without a database lookup.