Testing

Chapter 10
Testing, Tooling & Hardening Checklist
Finding SQLi, sqlmap, the broken-defence catalogue, and a deployable checklist

The finale turns the course into practice: how to test for SQLi, how the standard tooling works, the broken-defence patterns to recognize, and a single deployable checklist that pulls all ten chapters together. (Test only systems you own or are authorized to assess.)

How to Test for SQLi

  1. Map every input that reaches a query — URL params, form fields, JSON bodies, headers, cookies — and remember stored values too (second-order, Chapter 6).
  2. Probe injectability — a lone ' (error/changed behaviour), ' OR '1'='1 vs ' AND '1'='2 (logic change), arithmetic like 4+1 in numeric params (Chapter 2).
  3. Identify the context & type — string vs numeric (Ch. 2); in-band, blind, or out-of-band (Ch. 3) — this dictates the technique.
  4. Confirm impact safely — extract something harmless and unique (e.g. @@version / version()), not destructive payloads, to prove the finding.
  5. Check for blind — if no output/errors, test boolean (page difference) and time-based (SLEEP) inference (Chapter 5).
sqlmap — what it automates, and why that matters
sqlmap is the standard open-source SQLi tool. Pointed at a parameter, it automatically: detects whether it's injectable, determines the type (error/union/boolean/time/OOB) and the context, fingerprints the DBMS, enumerates the schema via information_schema, and dumps tables — and can escalate to reading files or OS commands where privileges allow. The lesson for defenders is sobering: everything attackers do in Chapters 2–6 is fully automated and requires no manual SQL. A single injectable parameter is, in practice, a one-command full-database disclosure. (Use sqlmap only against authorized targets / labs like PortSwigger, DVWA, Juice Shop.) Other tools: Burp Suite / OWASP ZAP scanners flag candidates; sqlmap confirms and exploits.

The Broken-Defence Catalogue

Broken practiceWhy it failsChapter
String-concatenated queriesthe root cause — input parsed as SQL1, 2
"We quote/strip quotes"numeric context needs no quote; fragile escaping2
Hiding DB errors as "the fix"only downgrades error-based to blind3
"No output, so it can't leak"time-based blind works on identical responses5
Validating only "user input"second-order injection from stored data6
String-formatting ≠ parameterizingf-strings/template literals bake value into SQL7
"Stored procedures are safe"concatenating procs are still injectable8
"We have a WAF"bypassable; blind to second-order8
"We use an ORM / NoSQL, so we're safe"raw queries; operator injection9

The Hardening Checklist

Parameterize every query — prepared statements / bound parameters everywhere; never concatenate or string-format input into SQL (the load-bearing fix, Ch. 7)
Allowlist dynamic identifiers — table/column/sort can't be parameterized; map user keys to known-good names
Parameterize reads of your own data too — trust by origin, not location (defeats second-order, Ch. 6)
Input validation (allowlist) — enforce expected types/formats; defence in depth, not the fix
Least-privilege DB accounts — minimal rights, no FILE/xp_cmdshell, scoped tables, read-only where possible (limits blast radius)
Generic errors to users, detailed logs server-side — reduces info leak (but isn't a fix)
ORM raw-query audit — grep the escape hatches; ensure bindings, not concatenation
NoSQL: validate types/structure — reject objects where scalars belong; forbid $where; schema enforcement
WAF as a backstop — perimeter layer only, never a substitute for fixing the code
Test it — probe inputs, run sqlmap against your own app, check blind & second-order paths
The one principle that survives the whole course
If you remember one thing: untrusted input must stay data and never become query code. Every SQLi — login bypass, union dump, blind inference, second-order, write/stack, even NoSQL operator injection — is one violation of that rule, and the one real fix (parameterized queries) is one expression of it: send the query and the data on separate channels so the data is parsed as data. Everything else — validation, least privilege, generic errors, WAFs — reduces likelihood and contains damage, but the load-bearing wall is keeping data off the code channel. This is the exact same principle as the XSS course (encode on output); injection is one family, and you now know two of its biggest members end to end.

How the Course Fits Together

The arc: understand the bug (Ch. 1 data-vs-code, Ch. 2 anatomy), the channels & techniques (Ch. 3 types, Ch. 4 union, Ch. 5 blind, Ch. 6 writes/stacked/second-order), then the defences — parameterized queries as the foundation (Ch. 7), defence in depth around it (Ch. 8), the realities of modern ORMs/NoSQL (Ch. 9), and operational testing/hardening (Ch. 10). It also pairs with the wider security set: SQLi and XSS are the two great injection bugs (database vs browser); Auth is what SQLi login-bypass attacks; and like all of them, the defence is a discipline applied everywhere, because the weakest query sets your exposure.

Hands-On Exercises

Exercise 1

Write a SQLi test plan for a single endpoint: how you'd map inputs (including stored/second-order), probe injectability, identify context and type, and safely confirm impact. Explain what sqlmap automates and why "a single injectable parameter" is a serious finding.

📄 View solution
Exercise 2

Audit this app against the broken-defence catalogue: it concatenates a numeric id, "strips single quotes" from inputs, hides DB errors, validates only request inputs (not stored data), uses a stored procedure that builds dynamic SQL, and relies on a WAF. List every flaw, its chapter, and the fix.

📄 View solution
Exercise 3

Produce a prioritized SQLi hardening plan for a new app (relational DB, an ORM, a couple of raw reporting queries, a search feature). Order the measures, justify the ordering, and explain how the one core principle (data vs code) unifies SQLi with XSS and connects to the Auth course.

📄 View solution

Chapter 10 Quick Reference

  • Test: map all inputs (incl. stored/second-order) → probe (', OR 1=1, arithmetic) → identify context/type → confirm safely (version()) → check blind
  • sqlmap automates detect → type/context → DBMS fingerprint → enumerate schema → dump tables (→ files/OS where allowed); a single injectable param ≈ full-DB disclosure
  • Broken defences: concatenation · quote-stripping · hiding errors · "no output" · validating only user input · string-formatting · "stored procs/WAF/ORM make us safe"
  • Checklist: parameterize everything · allowlist identifiers · parameterize reads of own data · validate types · least privilege · generic errors · ORM raw audit · NoSQL type validation · WAF backstop · test
  • The one principle: untrusted input stays data, never query code — parameterization sends query & data on separate channels
  • Defence in depth reduces likelihood/blast radius; the load-bearing wall is parameterized queries
  • Same principle as XSS (encode on output); SQLi & XSS are the two big injection families — database vs browser

★ SQL Injection Complete — 10 / 10 chapters

From data-vs-code foundations through injection anatomy, the in-band/blind/out-of-band types, union extraction, blind inference, writes/stacked/second-order, then the defences — parameterized queries as the one real fix, defence in depth, modern ORM/NoSQL realities, and a deployable testing & hardening checklist. Paired with HTTPS, CSRF, XSS, and Authentication, you now hold a five-course web-security set — and the unifying truth across all the injection bugs: keep untrusted input as data, never let it become code.