| name | second-order-injection-anti-pattern |
| description | Security anti-pattern for second-order injection vulnerabilities (CWE-89 variant). Use when generating or reviewing code that retrieves data from databases, caches, or storage and uses it in subsequent queries or commands. Detects trusted internal data used unsafely. |
Second-Order Injection Anti-Pattern
Severity: High
Summary
Malicious payloads are stored safely in databases or logs, then executed later when retrieved and used without re-sanitization. Initial storage appears secure (properly parameterized), but subsequent retrieval and unsafe use activates the payload. Injection and execution points are separated in time and code location, making detection difficult.
The Anti-Pattern
The anti-pattern is treating database-retrieved data as safe and using it in queries or commands without re-sanitization or parameterization.
BAD Code Example
import sqlite3
db = sqlite3.connect("app.db")
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
db.execute("CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, action TEXT, user_email TEXT)")
def register_user(name, email):
db.execute("INSERT INTO users (name, email) VALUES (?, ?)", (name, email))
db.commit()
def log_user_action(user_id, action):
cursor = db.execute("SELECT email FROM users WHERE id = ?", (user_id,))
user_email = cursor.fetchone()[0]
log_query = f"INSERT INTO logs (action, user_email) VALUES ('{action}', '{user_email}')"
db.execute(log_query)
db.commit()
GOOD Code Example
import sqlite3
db = sqlite3.connect("app_safe.db")
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
db.execute("CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, action TEXT, user_email TEXT)")
def register_user_safe(name, email):
db.execute("INSERT INTO users (name, email) VALUES (?, ?)", (name, email))
db.commit()
def log_user_action_safe(user_id, action):
cursor = db.execute("SELECT email FROM users WHERE id = ?", (user_id,))
user_email = cursor.fetchone()[0]
db.execute("INSERT INTO logs (action, user_email) VALUES (?, ?)", (action, user_email))
db.commit()
Detection
- Audit data flows: Systematically track data from its entry point (user input) through its storage and subsequent retrieval and use.
- Identify dynamic query/command construction: Look for any code that builds SQL queries, shell commands, or other interpretive language statements by concatenating strings that include variables whose values originated from user input, even if they were stored in a database.
- Review stored procedures: If your application uses stored procedures, examine their definitions for any dynamic SQL that might use input parameters without proper escaping or parameterization.
- Consider background jobs/asynchronous tasks: Pay special attention to components that process data in the background, as they might retrieve stored data and use it in new, insecure contexts.
Prevention
Related Security Patterns & Anti-Patterns
- SQL Injection Anti-Pattern: Second-order SQL injection is a variant of this fundamental vulnerability.
- Command Injection Anti-Pattern: Similar second-order risks exist when data stored safely is later used in an insecure shell command.
- Log Injection Anti-Pattern: Log files can be a vector for second-order attacks if logged data is later used in an insecure context (e.g., parsing logs with a vulnerable regex).
References