← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

SQL Injection

Security CWE-89 OWASP A3:2021 CVSS 9.8 PHP 5.1+ Intermediate
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, psalm, and semgrep — all specialist static analysis tools that can flag string-interpolated query patterns. Default linters do not catch this automatically, and it won't be caught by the compiler, so d5 is the right anchor. Not d7 because automated tooling is explicitly available and noted.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a clear one-component swap: replace $db->query('...{$var}') with PDO prepare/execute. However, common_mistakes note that table/column names, ORMs with raw methods, and second-order injection mean a thorough fix often touches multiple call sites across the codebase, nudging slightly above e1. e3 fits: it's a repeatable pattern replacement, not a full architectural refactor.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The applies_to covers web, cli, and queue-worker contexts broadly, so it's not confined to one spot. However, the fix is a well-understood pattern (prepared statements) that doesn't reshape the architecture. Once parameterised queries are adopted as a convention, the ongoing burden is low — the primary cost is auditing existing query call sites, not a persistent productivity tax on all future work.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field states that 'escaping quotes is sufficient protection' — a belief held by many competent developers familiar with older PHP patterns (addslashes, mysql_real_escape_string). This directly contradicts the expectation that sanitising input is equivalent to parameterisation. It also contradicts how similar concepts work in other contexts (e.g. HTML escaping is sufficient for XSS). Second-order injection adds another dimension of surprise. Not t9 because the primary defence (prepared statements) is widely documented and taught.

About DEBT scoring →

Also Known As

SQLi SQL attack database injection

TL;DR

Unsanitised user input inserted directly into a SQL query, letting attackers read, modify, or delete database data.

Explanation

SQL Injection occurs when user-supplied data is concatenated into a SQL statement without parameterisation. An attacker can close the intended query early and append their own SQL — extracting every row in the database, bypassing login checks, or calling destructive commands. It is consistently the most exploited web vulnerability. The fix is always prepared statements with bound parameters; input sanitisation alone is not sufficient.

How It's Exploited

GET /users?id=1 OR 1=1--
# Returns all rows

GET /users?id=1; DROP TABLE users--
# Drops the table (if stacked queries allowed)

Diagram

flowchart TD
    INPUT[User Input<br/>' OR 1=1 --] --> CONCAT[String Concatenation<br/>SELECT * FROM users<br/>WHERE name = '' OR 1=1--']
    CONCAT --> DB[(Database<br/>returns ALL rows)]
    DB --> LEAK[Data Leak]
    subgraph Fix
        SAFE_INPUT[User Input] --> PREP[Prepared Statement<br/>? placeholder]
        PREP --> BIND[Driver escapes<br/>automatically]
        BIND --> SAFE_DB[(Database<br/>safe query)]
    end
style LEAK fill:#f85149,color:#fff
style SAFE_DB fill:#238636,color:#fff

Common Misconception

Escaping quotes is sufficient protection. It isn't — prepared statements with parameterised queries are the only reliable defence, because escaping is encoding-dependent and easy to get wrong.

Why It Matters

SQL injection is the most exploited web vulnerability — a single unsanitised input can expose an entire database, bypass authentication, or destroy data. Parameterised queries cost nothing to implement and eliminate the risk entirely.

Common Mistakes

  • Using string escaping (addslashes, mysql_real_escape_string) instead of prepared statements — escaping is bypassable.
  • Parameterising values but dynamically concatenating table or column names into queries.
  • Assuming an ORM makes you immune — raw query methods like DB::statement() still allow injection.
  • Forgetting second-order injection: safely stored input can re-enter a query unsafely later.

Code Examples

✗ Vulnerable
$id = $_GET['id'];
$users = $db->query("SELECT * FROM users WHERE id = $id");
✓ Fixed
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => (int) $_GET['id']]);
$user = $stmt->fetch();

Added 13 Mar 2026
Edited 22 Mar 2026
Views 95
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings W 2 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 3 pings T 6 pings F 2 pings S 9 pings S 5 pings M 1 ping T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 2 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 1 ping M 1 ping T 0 pings W
No pings yet today
Sogou 1
Scrapy 27 Perplexity 11 Amazonbot 10 Ahrefs 7 Google 6 ChatGPT 3 Claude 2 Qwen 1 Bing 1 Meta AI 1 SEMrush 1 PetalBot 1 Sogou 1
crawler 68 crawler_json 4
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
Replace $db->query("...{$var}") with $pdo->prepare('...:p') + execute([':p'=>$var])
📦 Applies To
PHP 5.1+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
->query("...{$_GET" or ->query("...$_POST or ->query("...$variable
Auto-detectable: ✓ Yes phpstan psalm semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-89 CWE-564


✓ schema.org compliant