SQL Injection in Practice: Exploiting, Detecting and Mitigating in a Controlled Lab
Hands-on SQLi demo with sqlmap in your own lab, focused on defensive detection and parameterized fixes that actually hold up against production traffic.
SQL Injection turned 27 in 2026 and still sits in the OWASP top 3. Not because attackers got smarter, but because misused ORMs, dynamic queries in internal dashboards, and GraphQL endpoints with naive resolvers keep concatenating strings. The lab we build here starts with a DVWA instance running in Docker, MySQL 8.0 with binlog enabled, and a Burp Suite 2025.6 proxy capturing every request. The goal is not to flex a users table dump: it is to train your eye to spot the defensive instrumentation signals that stop sqlmap before the second payload. If you have not set up an isolated environment yet, swing by Web Pentesting From Scratch: Building a Safe Lab with DVWA, Juice Shop and Burp Suite before continuing.
The first experiment hits the DVWA GET-based SQLi endpoint at low level. We run sqlmap -u 'http://lab.local/vulnerabilities/sqli/?id=1&Submit=Submit' --cookie='PHPSESSID=...; security=low' --batch --technique=BEUST --level=3 --risk=2. In 4 seconds sqlmap identifies boolean-based blind injection in the id parameter, in 11 seconds it enumerates the dvwa database and its 2 tables. Notice the payloads being sent: 1 AND 4523=4523, 1 AND 1=2 UNION SELECT NULL,NULL. Those repetitive numeric literals are gold for WAF and SIEM detection rules. Note the default user-agent sqlmap/1.8.x and the missing Accept-Encoding header, because we will turn that into signal later.
Before scaling up to time-based techniques we open a second tab and enable query logging on MySQL with SET GLOBAL general_log = 'ON'. Every sqlmap payload shows up in /var/log/mysql/general.log with microsecond timestamps. Comparing 30 seconds of legitimate traffic (generated by a locust run simulating 50 users) against 30 seconds of sqlmap, the entropy of the queries explodes: the coefficient of variation of query length jumps from 0.12 to 1.8. That delta alone already feeds a decent Sigma rule. It pairs well with what we cover in Threat Hunting with Sigma and Elastic: From Indicator to Detection Rule to ship this as a reusable detection on Elastic.
Now the part that separates the script kiddie from the pentester: exploiting second-order SQLi. We register a user with the name admin'-- through the signup form, where the prepared statement safely escapes the quote. The problem lives in the internal search endpoint that reuses that value in a dynamic query without reparameterizing. Result: authentication bypass on a route that did not even show up in sqlmaps initial scan, because the payload only fires in a different context. That pattern is the same one that punctures GraphQL APIs with resolvers sharing builders, as detailed in REST and GraphQL API Pentest: Technical Checklist for Legal Bug Bounty. sqlmap will not catch this on its own, which is why manual code review keeps paying off in any bug bounty program.
Real mitigation starts with parameterized prepared statements, but it does not end there. In our Java/Spring lab we swap the interpolated string for NamedParameterJdbcTemplate, validate input with Bean Validation (jakarta.validation 3.1), and add an allowlist of permitted columns for ORDER BY (because PreparedStatement cannot parameterize identifiers). On PHP, PDO with PDO::ATTR_EMULATE_PREPARES=false is mandatory on MySQL to avoid the classic multibyte quote bypass. Next layers: least privilege on the application user (only GRANT SELECT, INSERT, UPDATE), plus a WAF like ModSecurity with CRS 4.4 in blocking mode for numeric parameters. Server hardening follows the same principle covered in Linux Server Hardening: Applying CIS Benchmark Without Breaking Production: shrink the attack surface before you trust detection.
To close the defensive loop we wire general_log into Filebeat, ship it to an Elastic 8.16 cluster, and build three detections: SQL syntax error spikes above 5 per minute per IP, queries containing UNION SELECT NULL in sequence, and query execution time above 3 standard deviations from the hourly baseline. In tests against sqlmap with --random-agent and --delay=2, all three rules fired in under 90 seconds. We also planted honeytokens: fake columns like credit_card_test with traceable strings. The day that value shows up on a paste site, we know exactly which endpoint leaked. That red and blue feedback loop mirrors what we argue for in Purple Team in Practice: Building a Red vs Blue Feedback Loop.
Practical takeaway: spin up the lab, run sqlmap once to feel the rhythm of its payloads, then spend 80 percent of the remaining time building detection and fixes. SQLi is not an attacker creativity problem, it is a dynamic query that nobody reviewed. Drop a quarterly review on your calendar covering every query that accepts external input, parameterize anything that is a value, and allowlist anything that is an identifier. The day you can prove your application kills sqlmap in under 2 minutes with automated blocking, you won.