From a single quote to full database dump - error-based, union, and blind SQLi, plus sqlmap and prevention.
Hard 30 minsqlidatabasesqlmap
SQL injection is the bug that has leaked more data than almost any other. It happens when user input is glued directly into a database query, letting an attacker rewrite what that query does - read other users' data, bypass logins, or dump the entire database. It's old, it's still everywhere, and it's a rite of passage for every web hacker.
Authorized targets only
Only practice SQLi against apps you own or are explicitly authorized to test - DVWA, OWASP Juice Shop, PortSwigger's labs, or a VM you control. Running these payloads against someone else's site is a crime.
When errors are shown, you can make the database leak data inside the error message (e.g. MySQL extractvalue/updatexml, or CAST on Postgres). Useful, but noisy.
If the query returns rows to the page, UNION SELECT lets you append your own result set. First find the column count:
1 ORDER BY 1-- 1 ORDER BY 2-- 1 ORDER BY 3-- -- errors when you exceed the column count
Then pull data into the visible columns:
0 UNION SELECT username, password FROM users--
Match the columns
UNION requires the same number of columns and compatible types. Use NULL placeholders (e.g. UNION SELECT NULL, username, NULL FROM users) until it renders cleanly.
Never concatenate. Use parameterized queries / prepared statements so input is always treated as data, never code.
# Python (psycopg / sqlite3) - the ? / %s is a bound parameter, not string formattingcur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (user, pw))
// PHP PDO$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");$stmt->execute([$user]);
Defense in depth adds: least-privilege DB accounts, input allowlists where structure is known, and an ORM used correctly (no raw string interpolation).