What Is SQL Injection? How It Works and How to Prevent It
SQL injection (SQLi) is a vulnerability where an attacker manipulates the SQL queries an application sends to its database by injecting malicious input into a place the developer expected to hold ordinary data. When the database treats that input as part of the query rather than as a value, the attacker can read, modify, or delete data they should never have been able to touch.
The root cause: mixing code and data
Almost every SQL injection flaw comes from the same mistake: building a query by concatenating untrusted input directly into a SQL string. The database receives one blob of text and cannot tell which parts were the developer's intended logic and which parts came from a user. Consider a login lookup built like this:
query = "SELECT * FROM users WHERE username = '" + name + "'"
If name is a normal value like alice, the query behaves as intended. But if a user submits ' OR '1'='1, the string becomes:
SELECT * FROM users WHERE username = '' OR '1'='1'
The condition '1'='1' is always true, so the WHERE clause matches every row. The user's quote character escaped the data context and the rest of their input was parsed as SQL. That is the entire mechanism in one example.
Why it matters
A successful injection can dump entire tables of credentials, payment details, or personal data; modify or delete records; and in some configurations escalate to running operating-system commands on the database host. Because injection happens through inputs that applications expose by design (search boxes, login forms, URL parameters, API bodies), it is reachable by anyone who can talk to the application. SQL injection has appeared on the OWASP Top Ten list of web application risks for many years, which reflects both how common it remains and how damaging it can be.
Common types of SQL injection
Injection techniques are usually grouped by how the attacker gets information back out of the database.
In-band (classic) injection
The results come back through the same channel the attacker used to send the payload. Two frequent variants are error-based injection, where database error messages leak data or structure, and UNION-based injection, where an attacker appends a UNION SELECT to pull columns from other tables into the application's normal output.
Blind injection
The application does not return query results or errors directly, so the attacker infers data one piece at a time. In boolean-based blind injection, the attacker crafts conditions that change the page's response when true versus false. In time-based blind injection, the payload tells the database to pause (for example with a sleep function) when a condition is true, and the attacker measures the response delay.
Out-of-band injection
When direct and blind channels are impractical, an attacker may force the database to make an outbound request (such as a DNS or HTTP lookup) carrying the stolen data. This depends on specific database features being available and is less common.
Where injection shows up
Any value that reaches a query is a candidate, not just obvious text fields. ORDER BY and LIMIT clauses, column and table names, numeric IDs in a URL, JSON fields in an API request, HTTP headers, and even cookies have all been injection vectors. Stored procedures and ORM "raw query" escape hatches are also vulnerable if they build SQL from strings internally. A common misconception is that only the login form matters; in practice, the report filter or the sort dropdown is just as exploitable.
How to prevent it
The defenses below are layered. The first one is the actual fix; the rest reduce impact when something slips through.
Use parameterized queries (prepared statements)
This is the primary, near-complete defense. With parameters, you send the query structure and the values to the database separately, so user input can never change the query's meaning. The same login example, written safely:
cursor.execute("SELECT * FROM users WHERE username = ?", (name,))
Here ? is a placeholder (some drivers use %s or named parameters like :name). The database compiles the statement first, then binds name strictly as a value. Even ' OR '1'='1 is treated as a literal username string and simply matches nothing.
Prefer query builders and ORMs correctly
Mature ORMs and query builders parameterize automatically for normal operations. The risk reappears only when you drop to raw SQL and interpolate variables yourself, so reserve raw queries for cases that genuinely need them and parameterize even those.
Validate and allowlist where parameters cannot reach
Some query parts (a column name in ORDER BY, a table name) cannot be bound as parameters. For those, validate the input against an explicit allowlist of permitted values rather than passing the raw string through.
Apply least privilege and defense in depth
Give the application's database account only the permissions it needs, so a breach of one query cannot drop tables or read unrelated schemas. Avoid surfacing raw database errors to users, since those messages aid error-based injection. None of these replace parameterization, but they limit the blast radius.
What does not work
Blocklisting "bad" words like SELECT or stripping single quotes is unreliable: encodings, comment syntax, and alternative phrasings bypass naive filters, and legitimate input (an apostrophe in a name) breaks. Escaping by hand is also fragile because the correct escaping rules differ by database and context. Parameterization sidesteps all of this by never letting input be parsed as code in the first place.
See injection in action and write safer SQL
The clearest way to build intuition is to watch a query mutate as input changes. Our interactive SQL Injection Demo shows how unescaped input rewrites a query, which makes the code-versus-data distinction concrete. SQL injection is also closely related to cross-site scripting, another injection class where untrusted input is parsed in a context that trusts it.
When you are reviewing real queries, a SQL Formatter makes the structure (and any odd concatenation) easier to spot, and the SQL to ORM converter can help move string-built statements toward parameterized model code. For the credential-storage half of database security, a proper password hasher ensures that even a leaked users table does not hand over plaintext passwords.
Frequently Asked Questions
Yes. Despite being well understood for over two decades, it remains common because many applications still build queries by concatenating user input. It continues to appear in the OWASP Top Ten of web application security risks.
Parameterized queries, also called prepared statements. They send the query and the user-supplied values to the database separately, so input is always treated as data and can never change the query's logic.
Mostly, but not automatically. ORMs parameterize standard operations for you, but the risk returns whenever you use a raw-query escape hatch and interpolate variables into the SQL string yourself. Parameterize those too.
No. Validation and allowlisting are useful defense-in-depth layers, but blocklisting keywords or stripping quotes is easily bypassed and can break legitimate input. Parameterization is the actual fix; validation supplements it.
It is injection where the application does not return query results or errors directly. The attacker infers data indirectly, for example by observing whether a page changes (boolean-based) or by measuring response delays from a database sleep command (time-based).