Detection Engineering

SQL Injection Detection: A Defensive Guide

How to detect SQL injection across web, app, and database telemetry — with Sigma, Suricata, and SPL rules, a CVE-2025-1094 case study, and tuning tips. Lab-only.

A SIEM log table with several SQL-error rows highlighted in red against fine cyan text

SQL injection detection works on three layers at once: the web request, the application error stream, and the database engine’s own behavior. A WAF signature alone misses blind and second-order attacks. The durable signal is correlation — a spike of database syntax errors from one source IP, an application account suddenly running UNION SELECT, or a database process spawning a shell. This guide ships the rules to catch all three, plus how to tune and test them.

SQL injection still sits at the top of the OWASP Top 10 as A03:2021 — Injection, and it still ends up in nation-state breach chains. In February 2025, Rapid7 disclosed CVE-2025-1094, a PostgreSQL injection flaw that was a required link in the BeyondTrust exploit chain behind the US Treasury intrusion. We use it as the worked example, because it breaks an assumption most teams still hold.

What is SQL injection, in a defender’s terms?

SQL injection is an attack where untrusted input changes the structure of a database query instead of just its data. The application meant to ask “find user 123”; the attacker rewrites the question into “find user 123, and also dump every password.” You do not need to write payloads to detect them — you need to know the shapes they leave behind in your telemetry.

A successful injection rarely stays “just” a data leak. The same flaw that dumps a users table can pivot to code execution through database features like xp_cmdshell on SQL Server or psql meta-commands on PostgreSQL. That is the difference between an incident report and a breach notification, and it is why detection has to reach the database tier, not stop at the WAF.

What are the four types of SQL injection?

There are four families, and each leaves a different telemetry fingerprint. The table below is the cheat sheet I keep next to the SIEM — it maps each variant to the signal it produces and the layer that catches it best.

VariantWhat the attacker doesTelemetry fingerprintBest detection layer
Error-basedProvokes verbose DB errors to leak schema/dataBurst of HTTP 500s + DB syntax errors from one IPWeb / app logs
Union-basedAppends attacker columns onto a real result setResponse row count or byte size far above baselineNetwork + DB
Boolean-blindAsks true/false questions one bit at a timeMany near-identical requests, two alternating sizesWeb behavioral
Time-based blindInjects a sleep, reads the answer from latencyRepeated requests with round-number latency (5.0s)Web behavioral

The lesson for detection design: signatures catch the loud variants (error- and union-based), but blind injection only shows up in behavioral and timing analytics. You need both, which is exactly why this guide layers them.

How to detect SQL injection across three layers

Layer the detection the way the attack surface is layered: web, network, and the database tier most teams skip. The same philosophy drives every rule on darkpwn — behavior over brittle strings, the way the detection-as-code workflow for Sigma rules lays out.

Web tier: signature and rate detection

The cheapest sensor is the access log. The Sigma rule below flags injection tokens as a triage signal for correlation — the WAF does the blocking, this gives your SIEM the event to correlate.

Sigma Web Request Containing SQL Injection Tokens
title: Web Request Containing SQL Injection Tokens
id: 7b3f1d2a-darkpwn-illustrative
status: experimental
logsource:
  category: webserver
detection:
  selection:
    cs-uri-query|contains:
      - 'union select'
      - "' or 1=1"
      - 'sleep('
      - 'pg_sleep('
      - 'waitfor delay'
      - 'information_schema'
  condition: selection
falsepositives:
  - Security scanners and DAST runs sending benign test strings
level: medium

That single-event rule is noisy alone. The higher-fidelity web signal is rate: count database syntax errors per source IP per minute — the error-based fuzzing fingerprint — and only alert above a threshold. A login form that throws 10 SQL syntax errors a minute from one IP is being fuzzed, not used.

Network tier: a Suricata signature for union-based attacks

If you run an IDS between tiers, a Suricata rule catches the union pattern on the wire before it reaches the app. Reserve SID range 1000001+ for your own rules.

Suricata UNION SELECT in HTTP Request
alert http any any -> $HTTP_SERVERS any (
    msg:"DARKPWN SQLi UNION SELECT in HTTP request";
    flow:established,to_server;
    http.uri; content:"union"; nocase;
    content:"select"; nocase; distance:0; within:30;
    classtype:web-application-attack; sid:1000001; rev:1;)

Network rules see only what isn’t TLS-terminated upstream. If your edge terminates TLS, run Suricata against the decrypted span or lean on layers 1 and 3 — and write the gap into your coverage matrix so a blind spot doesn’t read as coverage.

Database tier: the detection most teams skip

This is the highest-value layer and the one most often missing. A database engine has no business launching a shell — when it does, treat it as an incident, not an alert. This is the post-exploitation stage CVE-2025-1094 reached.

Sigma Database Process Spawned a Command Shell
title: Database Process Spawned a Command Shell
id: 2d8a4c11-darkpwn-illustrative
status: experimental
logsource:
  category: process_creation
detection:
  parent:
    ParentImage|endswith: ['\sqlservr.exe', '\postgres.exe', '\mysqld.exe', '\psql.exe']
  child:
    Image|endswith: ['\cmd.exe', '\powershell.exe', '\bash', '\sh']
  condition: parent and child
falsepositives:
  - Rare DBA jobs or backup scripts that shell out (allowlist them)
level: high

How to test your SQL injection detection

A detection you have never watched fire is a hypothesis, not a control. Validate each rule in a lab you own before you trust it in production:

  1. Stand up a deliberately vulnerable app — OWASP Juice Shop or DVWA in a throwaway VM, never against a system you don’t own.
  2. Generate each variant against it: error-based, union-based, and both blind types. Confirm the matching rule fires on the true positive.
  3. Replay a normal baseline — real user traffic, scanners you’ve allowlisted, your own DAST run — and confirm the rule stays quiet. A rule that fires on your weekly scan will be muted within a week.
  4. Tune the thresholds to your endpoint’s real error rate, then re-run both passes. Record the false-positive sources in the rule itself.

This is the same fire-it-before-the-adversary-does discipline behind Sigma rules that actually fire.

How to prevent SQL injection

Detection buys you time. Prevention removes the bug class. Deploy both, in this order of impact.

  • Treat the WAF as defense in depth, not the control. Encoding tricks and blind variants slip past; CVE-2025-1094 had nothing to match against.
  • Patch the data tier, not just the app tier. CVE-2025-1094 was fixed in PostgreSQL 17.3, 16.7, 15.11, 14.16, and 13.19. Track database-engine and driver CVEs with the same urgency as application CVEs.
  • Validate input at the boundary as a noise reducer, never as the only line — an integer ID should be an integer.

Common SQL injection detection mistakes

These are the gaps I see most often when reviewing a SIEM that “already covers SQLi”:

  • WAF-only coverage. The WAF blocks the obvious and logs nothing useful about the rest. Forward app and database telemetry too.
  • No database-tier visibility. Without process-creation and query-audit logs, the highest-fidelity signal — a DB engine spawning a shell — is invisible.
  • Ignoring second-order injection. The payload is stored clean and fires later from a different code path. Request-time rules see nothing; only DB verb/volume anomalies catch it.
  • Untuned thresholds. A rule that pages on every DAST scan gets muted, and a muted rule detects nothing.

SQL injection detection checklist

Copy this into your detection backlog and check it off:

  1. Parameterize every query; ban string concatenation in code review.
  2. Run the app’s DB account at least privilege — no FILE, no xp_cmdshell.
  3. Forward web access logs, application error logs, and DB audit logs to the SIEM.
  4. Deploy the web-token Sigma rule plus a per-IP DB-syntax-error rate threshold.
  5. Deploy the database-process-spawns-a-shell rule — the highest-fidelity signal.
  6. Add the Suricata UNION SELECT rule between tiers where you have TLS visibility.
  7. Alert on verb anomalies (DROP/xp_cmdshell from a web account) and 50× row-count spikes.
  8. Patch database-engine and driver CVEs (e.g. CVE-2025-1094) on the app-CVE cadence.
  9. Fire every rule in a lab against true-positive and benign traffic; record false positives.

The takeaway

You cannot prove the absence of injection, so you instrument for its presence. Web signatures catch the loud attempts, behavioral analytics catch the blind ones, and a database process spawning a shell catches the one that already won. Parameterize to remove the bug class; detect to survive the regression. For the rest of the web-application attack surface, the same detect-and-defend arc carries into SSRF detection and XSS CSP hardening, and you can browse the full Detection Engineering pillar for more.

Training & tools referenced

Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.

  • TryHackMeAuthorized labs to practice these SQLi detections against real telemetrySecurity Training
    Start training

Frequently asked questions

How do you detect SQL injection in logs?

Watch for a burst of database syntax errors ("unclosed quotation," "unterminated quoted string") from one source IP, requests carrying UNION SELECT or OR 1=1, and round-number response latencies that signal time-based blind injection. The highest-fidelity signal is a database process spawning a shell.

Can a WAF stop all SQL injection?

No. A WAF blocks obvious payloads at the edge and is worth running as defense in depth, but blind, second-order, and encoding-based variants slip past tuned-down rules. CVE-2025-1094 is the proof: the payload was valid escaped output, so a content-matching WAF had nothing to match. Parameterized queries are the real fix.

What is the difference between error-based and blind SQL injection?

Error-based injection leaks data through verbose database errors and is loud in logs. Blind injection reads answers indirectly, from page differences (boolean) or response delays (time-based), and produces no errors. Blind variants need behavioral and timing detection, not signatures.

Which MITRE ATT&CK technique covers SQL injection?

SQL injection maps to T1190 (Exploit Public-Facing Application). When it pivots to code execution through database features like xp_cmdshell or psql meta-commands, add T1059 (Command and Scripting Interpreter).

Newsletter

Liked this breakdown?

Defensive security research — detection, hardening, and hardware — delivered when there is something worth saying. No spam, unsubscribe anytime.