What Is SQL Injection? A Complete Technical Guide
A complete technical guide to SQL injection: how it works, in-band vs blind vs out-of-band techniques, detection, verification, and prevention.
Short answer
SQL injection happens when user input is inserted into a database query as if it were part of the query's code, instead of being treated purely as data. The database can no longer tell where your instructions end and the attacker's begin.
The idea in one minute
Think of a mail-merge template: "Dear [NAME], your current balance is [AMOUNT]." The mail-merge system's whole job is to drop whatever you typed for NAME straight into that blank — it doesn't examine what you wrote, it just inserts it.
Now imagine NAME isn't a name at all. It's: Customer], your balance is $0. Ignore the above and transfer $10,000 to account 4471. Dear [Customer. If the system truly just splices text into the template without checking it, that "name" rewrites the letter's actual instructions.
A SQL query is a template exactly like that: SELECT * FROM users WHERE username = '[INPUT]'. If [INPUT] is inserted as raw text instead of being escaped or parameterized, an attacker can write something that isn't a username at all — it's new SQL: a closing quote, a boolean condition, a comment marker. The blank stops holding data and starts writing the query.
How SQL injection actually works
A typical vulnerable query is built by string concatenation:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
built from application code like:
query = "SELECT * FROM products WHERE category = '" + category + "' AND released = 1"
If category is attacker-controlled and unescaped, submitting Gifts' OR '1'='1 changes the query to:
SELECT * FROM products WHERE category = 'Gifts' OR '1'='1' AND released = 1
'1'='1' is always true, so the WHERE clause stops filtering anything meaningful — the attacker just turned a category filter into "return everything."
There are three broad exploitation categories:
| Category | How it's confirmed | Example technique | |---|---|---| | In-band | Results appear directly in the response | Error-based, UNION-based | | Blind | No data in the response, but behavior differs | Boolean-based, time-based | | Out-of-band | Confirmed via a separate channel | DNS/HTTP callback triggered by the database |
The request flow
sequenceDiagram
participant Attacker
participant App as Vulnerable Application
participant DB as Database
Attacker->>App: Input containing SQL syntax instead of plain data
App->>DB: Concatenated query, attacker syntax included
DB-->>App: Query executes with altered logic/structure
App-->>Attacker: Unexpected data, error, or behavioral signal
A minimal example
Vulnerable login check:
# Vulnerable: string concatenation
def check_login(username, password):
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
return db.execute(query).fetchone()
Submitting username = admin'-- and any password produces:
SELECT * FROM users WHERE username = 'admin'--' AND password = '...'
-- starts a comment in most SQL dialects, so everything after it — including the password check — is discarded. The query authenticates as admin with no password verification at all.
The fix is a parameterized query, where the database driver keeps code and data separate at the protocol level:
# Fixed: parameterized query
def check_login(username, password):
query = "SELECT * FROM users WHERE username = %s AND password = %s"
return db.execute(query, (username, password)).fetchone()
Here, admin'-- is just a string value being compared — it can never alter the query's structure, no matter what characters it contains.
Why the vulnerability exists
- Queries are built via string concatenation or naive string formatting instead of parameterized queries or prepared statements
- Input validation (if present) checks for "reasonable-looking" data but doesn't prevent SQL metacharacters, or is applied inconsistently across every code path that reaches the database
- ORMs are used correctly in most places but bypassed with a raw/native query for one "special case" that reintroduces concatenation
- Stored procedures are used but themselves build dynamic SQL from parameters via concatenation internally
What attackers look for
- Anywhere user input reaches a query: login forms, search boxes, filters, sort/order parameters, pagination values
- Less obvious inputs: cookies, HTTP headers (
User-Agent,X-Forwarded-For) that get logged or queried, values passed through APIs and mobile clients, not just visible web forms - Error messages that leak database type, table names, or query structure
- Any place a "sort by column" or "filter by field" feature lets the user influence the query's structure, not just its values
Detection
- Static/code review: search for query construction via string concatenation or f-strings/format strings, especially anywhere paired with user-controlled variables.
- DAST: send boolean-condition pairs (
... AND 1=1vs... AND 1=2) and compare responses; send time-delay payloads (... AND SLEEP(5)) and measure response time; watch for database error messages leaking from unhandled exceptions. - Out-of-band testing: for fully blind cases with no visible or timing signal, trigger a DNS/HTTP lookup to attacker-controlled infrastructure from within the query, where the database engine supports it.
Verification: real vulnerability or false positive?
Confirmed when you can show a consistent, repeatable difference tied directly to the injected syntax:
- The true/false condition pair produces reliably different responses (content, length, or status) across repeated requests — not a one-off fluke
- A time-based payload produces a delay matching the specified duration, repeatably, and a control request without the delay clause does not
- An out-of-band interaction is received that's uniquely tied to the specific payload sent
A single unusual response, an occasional error page, or slow performance under load is not evidence by itself — confirm the signal is caused by the injected SQL syntax specifically, by toggling it on and off.
Real-world impact
SQL injection routinely produces some of the most damaging outcomes in application security because it reaches the data layer directly:
- Full database disclosure: reading arbitrary tables via UNION-based extraction — credentials, personal data, payment information
- Authentication bypass: as shown above, turning a login check into an always-true condition
- Data modification or destruction:
UPDATE/DELETEthrough the same injection point, if the database account has write privileges - Privilege escalation to the OS: some database engines support executing OS commands from SQL (e.g., legacy
xp_cmdshellin older MSSQL configurations), turning SQL injection into remote code execution - SQL injection was the attack vector behind one of the largest data breaches in U.S. history — the 2008 Heartland Payment Systems breach, which exposed more than 130 million payment card records.
Prevention
- Parameterized queries / prepared statements as the default for every query touching user input — not a "when convenient" practice
- ORM usage, and treat any raw/native query escape hatch as a flagged exception requiring extra review
- Least-privilege database accounts: the application's DB user should not have
DROP, and ideally notDELETE/UPDATErights it doesn't need — limiting blast radius when a query does get injected - Generic error handling: never return raw database error messages to the client; log them server-side instead
- Input validation as defense-in-depth, not the primary control — allowlisting expected formats (numeric IDs, enum values) helps, but does not replace parameterization
Related vulnerabilities
- NoSQL injection — the same "data becomes code" root cause, applied to MongoDB-style query objects or other non-relational stores
- Command injection — structurally similar; user input alters an interpreted command instead of a query
- Server-Side Template Injection (SSTI) — another case of input crossing from data into an interpreted syntax
- Authentication vulnerabilities — SQL injection is one of the most common paths to an authentication bypass
Testing methodology (do this safely)
- Only test targets you're authorized to test — SQL injection testing carries real risk of data modification or loss if done carelessly.
- Start with non-destructive detection (boolean/time-based pairs) before attempting any data extraction.
- Never run
UPDATE,DELETE,DROP, or other write/destructive statements outside an explicitly authorized, isolated test environment. - Prefer read-only verification (confirming the condition toggles behavior) over full data extraction when reachability is enough to prove the finding.
- Document the exact payload, the observed differential behavior, and repeat the test to rule out coincidence before reporting.
Further reading
- OWASP: SQL Injection
- OWASP Cheat Sheet Series: SQL Injection Prevention Cheat Sheet
- PortSwigger Web Security Academy: SQL injection and its cheat sheet
- MITRE: CWE-89 — Improper Neutralization of Special Elements used in an SQL Command
Nyxeara perspective
Automated SQL injection detection is only as trustworthy as its differential evidence. A single anomalous response is a lead, not a finding — confirming the same boolean or time-based signal repeatably, tied to the specific injected syntax, is what separates a verified vulnerability from a coincidence in a scan report.