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

Second-Order SQL Injection

security CWE-89 OWASP A3:2021 CVSS 8.8 PHP 5.0+ Advanced

Also Known As

stored SQL injection second-order injection persistent SQLi

TL;DR

Malicious data is safely stored in the database but later retrieved and used unsafely in a subsequent SQL query.

Explanation

Second-order (or stored) SQL injection occurs when user input is properly escaped on first insertion but later retrieved from the database and interpolated into another query without re-escaping. Developers often trust data already in their database, forgetting it may be attacker-controlled. The fix is identical to first-order injection prevention: use parameterised queries and prepared statements everywhere data is used in a query, regardless of its origin.

How It's Exploited

A user registers with the username admin'--. The registration safely escapes the apostrophe. Later, an admin panel retrieves the stored username and embeds it directly in a query like UPDATE users SET password='new' WHERE username='admin'--', commenting out the WHERE clause.

Common Misconception

Data that was safely inserted into the database is safe to use in future queries. Second-order SQLi stores malicious input correctly, then injects it when the stored value is later retrieved and concatenated into another query without parameterisation.

Why It Matters

Second-order SQL injection stores a malicious payload safely, then uses it in an unsafe query later — passing the first validation layer but exploiting the second interaction.

Common Mistakes

  • Trusting data retrieved from the database as 'safe' because it was stored via parameterised query.
  • Not parameterising queries that use database-sourced values — escaped on insert, concatenated on use.
  • Username/email stored safely but used as-is in dynamic SQL in stored procedures or admin queries.
  • Not auditing code that reads from the DB and then uses the value in another query.

Code Examples

✗ Vulnerable
// Safe insert — parameterised:
$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (?)');
$stmt->execute(["' OR '1'='1"]); // Stored safely

// Unsafe later use — second-order injection:
$name = $pdo->query('SELECT name FROM users WHERE id = 1')->fetchColumn();
$users = $pdo->query("SELECT * FROM logs WHERE user = '$name'"); // SQLI!
✓ Fixed
// Always use prepared statements at query time — even for 'stored safe' data:
public function promoteToAdmin(int $userId): void {
    // Even if $userId came from the DB and looks safe:
    $stmt = $this->pdo->prepare(
        'UPDATE users SET role = ? WHERE id = ?'
    );
    $stmt->execute(['admin', $userId]);
}

// Fetch then re-use safely:
$username = $this->pdo
    ->prepare('SELECT username FROM users WHERE id = ?')
    ->execute([$id])
    ->fetchColumn();

// Now use it safely in another query:
$stmt = $this->pdo->prepare('SELECT * FROM logs WHERE username = ?');
$stmt->execute([$username]); // Prepared — safe regardless of stored content

Added 15 Mar 2026
Edited 22 Mar 2026
Views 21
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 2 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S
No pings yet today
Amazonbot 7 Google 2 Perplexity 2 Unknown AI 2 ChatGPT 2 Ahrefs 1
crawler 13 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Use prepared statements for every database operation including reads of previously stored data — safe storage does not make data safe for later reuse in queries
📦 Applies To
PHP 5.0+ web cli
🔗 Prerequisites
🔍 Detection Hints
Data read from DB then concatenated into another query without parameterisation; stored username used in dynamic SQL later
Auto-detectable: ✗ No semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: File Tests: Update
CWE-89 CWE-564

✓ schema.org compliant