SQL injection testing: find queries built from input
SQL injection testing comes down to one job: find the queries your app builds by joining strings with user input. If a query pastes in text from a form field, a URL, a header or a cookie, an attacker can change what that query means. This guide shows you how to find those queries, confirm one safely on a test copy, and fix it the way OWASP recommends. Run it only on systems you own or have written permission to test.
What SQL injection testing looks for
You are hunting for one pattern: input that becomes part of the query instead of staying a value. The OWASP SQL Injection Prevention Cheat Sheet puts it plainly. An app is open to SQL injection when it has dynamic queries built with string concatenation and user input. OWASP also notes these flaws are very common, and that databases are a frequent target because they hold the data that matters.
So testing is not about memorizing clever strings. It is about tracing where data enters, following it to the database, and checking whether the line between code and data holds.

What to look for, and what OWASP says fixes it. Simplified from the OWASP SQL Injection Prevention Cheat Sheet.
Map every place input reaches a query
Start with a list, not a tool. Walk the app and write down every input that could end up in a database call:
- Form fields: login, search, sign-up, filters.
- URL parameters and path segments, such as
/products?id=42or/users/42. - Sort and filter options, such as
?sort=price&order=desc. - Headers and cookies your code stores or looks up.
- JSON bodies, including nested fields.
- Values from your own database that get used in a later query.
Mark the sort options. Table names, column names and sort order cannot be passed as query parameters, so they are where string-built SQL most often survives in apps that are otherwise careful.
Read the code: spot string-built SQL fast
If you have the source, reading it is the fastest test. Search for the functions that run queries in your stack, then look at how each query is built. Three shapes are red flags:
- A query joined with
+, string formatting or a template string that includes a variable. - Your ORM's raw query helper called with a string built from input. One raw call can undo a codebase full of safe ones.
- Dynamic SQL inside a stored procedure. OWASP tells reviewers to look for
sp_execute,executeorexecin SQL Server procedures.
A query with placeholder markers, where the value is passed separately, is the safe shape. The database driver, not your string logic, decides where the value goes.
Worked example: confirm one finding on a test copy
Say your product page runs this in a Node handler:
db.query("SELECT name, price FROM products WHERE category = '" + req.query.category + "'")
The code review already shows the flaw. To confirm it in the running app, use a staging copy with test data, never production, and keep every request read-only:
- Baseline. Request
?category=booksand note how many products come back. - Break the syntax. Request
?category=books'with one extra quote. A server error or a changed page, where a normal value works, is a signal. - Always false. Request
?category=books' AND '1'='2. If the list comes back empty, your text changed the query's logic. - Always true. Request
?category=books' AND '1'='1. If the normal list returns, the pair proves it. - Stop there. A repeatable true and false difference is the finding. Do not read other tables or change data.
Write down each request and response. After the fix, send the same requests again: both should now return the empty result for a category with that literal name, and the quote should cause no error.
Fix it with parameterized queries
OWASP lists four primary defences. Prepared statements with parameterized queries come first. Stored procedures are next, if they are written without dynamic SQL. Allow-list validation is third, for the parts of a query that cannot be parameters. Escaping all input is last, and OWASP strongly discourages it as fragile and unable to stop every injection.
The safe version of the example is a fixed query with a placeholder:
db.query("SELECT name, price FROM products WHERE category = $1", [req.query.category])
OWASP's own example shows why it works. With a parameter, an input like tom' or '1'='1 is matched as a literal name. It cannot change what the query does. Most languages and ORMs support parameters, including query languages that sit on top of SQL.
For the sort order, map input to values written in your code. Accept asc or desc, turn it into a true or false, and pick the SQL keyword from that. For a column name, use a switch over known names and reject anything else. OWASP also adds a warning: if user input picks table names at all, consider a redesign.
Limit the damage with least privilege
OWASP recommends extra layers even when every query is parameterized:
- Give each app's database account only what it needs. A screen that only reads gets read access to the tables it uses.
- Never give an app account DBA or admin rights, and do not share one owner account across apps.
- Use views to expose only the fields an account needs, and grant access to the view instead of the table.
- Do not run the database service itself as root or system.
- Keep allow-list input validation as a second check; OWASP recommends it in all cases. See input validation: check what your API accepts.
Also keep raw database errors out of responses. An error message that shows the query gives an attacker a map.
Add SQL injection checks to your regular testing
The first step, mapping where input goes, is shared with other injection bugs. While you have the map open, check the same inputs for XSS and template injection. Then add a release check: search new code for raw query calls and string-built SQL, and re-run your confirmed requests after every fix.
Frequently asked questions
Do ORMs prevent SQL injection?
An ORM's normal query builder passes values as parameters, which blocks most injection. The risk returns when you use its raw query feature with a string built from input, so review those calls one by one.
Is escaping quotes enough?
No. OWASP strongly discourages escaping as the main defence because it depends on the database and OWASP cannot promise it stops every injection. Use parameterized queries instead.
Are stored procedures safe from SQL injection?
Only when they do not build dynamic SQL inside. A procedure that pastes input into a string and runs it with exec has the same flaw as application code.
Can I test my production site?
Test a staging copy with test data instead. Confirmation needs only a pair of harmless requests, and there is no reason to risk real data.
Get started
Pick one feature with a search box or a sort option. Find its query in the code, run the five-step confirmation on staging, and fix any string-built query with a parameter or an allow-list. Then repeat for the next feature on your input map.
If you want a second pair of eyes, whitehatstoic's security testing covers web app and API review, with a written report, fixes and a retest after fixes. It is scoped after a short call, and no test can promise to find every weakness.

Building something that has to be safe? Book a meeting with whitehatstoic: tell us the product, the deadline and your biggest worry, and we reply with a plan and a price.
Comments
No comments yet.
Sign in or make an account to comment.