SQL Injection Attacks

SQL injection (SQLi) is a code-injection vulnerability in which attacker-controlled input is concatenated into a SQL query, causing the database to execute unintended commands. Because SQL makes no real distinction between the control plane (query structure) and the data plane (literal values), metacharacters in user input can break out of their intended context and alter the query’s logic. 1

SQLi is listed in the OWASP Top 10 under “A03:2021 – Injection.” A successful exploit can read sensitive data, modify or delete data, bypass authentication, execute administrative operations, read files from the DBMS filesystem, and in some cases issue commands to the operating system.

Detection

The canonical test is submitting a single quote (') and watching for errors or anomalies. PortSwigger’s systematic methodology adds:

  • Quote characters', ", and backtick, depending on context
  • Boolean conditionsOR 1=1 vs OR 1=2; look for systematic response differences
  • Time-delay payloadsSLEEP(10), pg_sleep(10), WAITFOR DELAY '0:0:10'; measure response time deltas
  • Out-of-band (OAST) payloads — trigger DNS/HTTP interactions to an attacker-controlled listener

SQL values can be enclosed in parentheses (treated as sub-queries), so injection contexts are not always simple string literals. Test all three quote types and parenthesized contexts. 2

Comment truncation

Most SQLi payloads end with a comment sequence to neutralize the remainder of the original query:

DatabaseComment syntax
MySQL-- (note trailing space), #, /* */
PostgreSQL--, /* */
Microsoft SQL Server--, /* */
Oracle--

URL-encoded variants (--+, --%20, --+-) are often needed because a trailing space may be stripped. If the developer filters trailing comments, append anything after the -- — it’s all a comment anyway. Sometimes no comment is needed when injecting at the end of a statement.

Types of SQLi

Error-based (in-band)

The database returns error messages containing query output. Requires verbose errors to be shown to the user. Microsoft SQL Server’s conversion errors and PostgreSQL’s cast errors are classic vectors; MySQL’s EXTRACTVALUE() and UPDATEXML() XPath errors serve the same role.

Union-based (in-band)

Abuses the UNION keyword to append attacker-controlled SELECT results to the original query. First determine the column count by iteratively adding NULL columns until the query succeeds:

' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--

Then probe column types by replacing NULL with a string literal one position at a time. Useful MySQL functions: database(), user(), @@version, GROUP_CONCAT() (aggregates multiple rows into one string), CONCAT() with 0x3a for colon separators.

Boolean-based blind

No query output is returned, but the application’s response differs based on a true/false condition. Inject conditional logic (e.g., AND (SELECT COUNT(*) FROM users) > 0) and observe differences in HTTP status codes, page content, or redirect behavior. Some frameworks try to defeat this with uniform redirects; Burp Suite can follow redirects and diff responses.

Time-based blind

No output and no boolean differential — only timing. Inject a conditional delay:

' AND IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0)--    -- MySQL
'; IF (1=1) WAITFOR DELAY '0:0:5'--                      -- MSSQL
' AND (SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END)--  -- PostgreSQL

A measurable delay confirms the condition is true. Extract data one bit at a time.

Out-of-band (OOB)

The database makes an external network call (DNS, HTTP, SMB) carrying exfiltrated data. Requires the DBMS to support network-accessible functions and the network to permit outbound traffic. DNS is the most reliable OOB channel because it typically traverses firewalls. Oracle’s UTL_HTTP, MSSQL’s xp_dirtree, and MySQL’s LOAD_FILE() with UNC paths are common vectors.

Authentication bypass

Login forms query the database for matching credentials; injecting a tautology returns true without knowing a password. Sometimes multiple fields must be injected simultaneously. Tack on LIMIT 1 when the application expects a single row.

Injection contexts beyond WHERE

Most SQLi occurs in WHERE clauses of SELECT queries, but injection can also land in:

  • UPDATE values or WHERE clause
  • INSERT values
  • Table or column names (rare, requires allow-listing)
  • ORDER BY clause (cannot use parameterized queries for identifiers — a common developer mistake)

Defense

Two complementary strategies:

  1. Parameterized queries (prepared statements) — the primary defense. The query structure is sent to the database separately from the data, making injection impossible. Stored procedures provide similar guarantees when implemented correctly.
  2. Contextual output encoding / escaping — escape user input for the specific SQL context (string literal, identifier, numeric). Know which context your data lands in and escape when crossing boundaries. Escaping alone is fragile; always prefer parameterization.

Additional controls: least-privilege database accounts, WAF rules, input validation as defense-in-depth, and avoiding dynamic SQL construction entirely.

Common language/database stacks

Certain pairings appear frequently in the wild and shape which payloads to try first:

Language / FrameworkTypical Database
PHPMySQL / MariaDB
.NET (ASP.NET)Microsoft SQL Server
Python (Django, Flask)PostgreSQL, MySQL, SQLite
Java (Spring, J2EE)Oracle, PostgreSQL, MySQL
Ruby on RailsPostgreSQL, MySQL, SQLite

SQLi is historically most common in PHP apps due to the prevalence of older functional interfaces; J2EE and ASP.NET’s programmatic interfaces make SQLi less likely but not impossible. 3

Sources

See also

Footnotes

  1. SQL Injection — OWASP

  2. SQL injection — PortSwigger Web Security Academy

  3. SQL Injection — OWASP