Injection Remediation
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker's hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization.
Why Injection Is Critical
SQL Injection (SQLi)
SQL injection occurs when user input is concatenated directly into a database query. The primary defense is the use of parameterized queries (also known as prepared statements).
Vulnerable Examples
Python (Vulnerable):
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)Java (Vulnerable):
String query = "SELECT * FROM users WHERE id = " + userId;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);PHP (Vulnerable):
$query = "SELECT * FROM users WHERE id = " . $_GET['id'];
$result = mysqli_query($conn, $query);Node.js (Vulnerable):
const query = `SELECT * FROM users WHERE id = ${req.params.id}`;
db.query(query);Secure Implementation: Parameterized Queries
๐ How Parameterized Queries Protect You
Parameterized queries (prepared statements) separate the SQL code from the data. The database driver handles escaping and quoting automatically, so user input is always treated as data, never as executable SQL code.
When you use ? or :param placeholders, the database compiles the query structure
first, then safely binds the values. Even if an attacker submits ' OR 1=1 --, it's treated
as a literal string, not SQL syntax.
Python (sqlite3):
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))Python (SQLAlchemy ORM):
user = session.query(User).filter(User.id == user_id).first()Java (Prepared Statement):
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setInt(1, userId);
ResultSet rs = pstmt.executeQuery();PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
$user = $stmt->fetch();Node.js (mysql2):
const [rows] = await connection.execute(
'SELECT * FROM users WHERE id = ?',
[req.params.id]
);C# (Entity Framework):
var user = context.Users.FirstOrDefault(u => u.Id == userId);SQL Injection Prevention Checklist
- โ Use parameterized queries / prepared statements for ALL database queries
- โ Use ORM frameworks (SQLAlchemy, Hibernate, Entity Framework, Sequelize)
- โ Implement input validation (whitelist allowed characters)
- โ Apply principle of least privilege to database accounts
- โ Escape special characters if dynamic queries unavoidable
- โ Use stored procedures with parameterized inputs
- โ Enable WAF rules for SQL injection patterns
- โ Regular code review and SAST scanning
Command Injection
Command injection occurs when an application passes unsafe user supplied data (forms, cookies, HTTP headers, etc.) to a system shell.
Vulnerable Examples
Python (Vulnerable):
import os
os.system(f"ping -c 4 {user_input}")Node.js (Vulnerable):
const exec = require('child_process').exec;
exec(`ping -c 4 ${userInput}`);PHP (Vulnerable):
system("ping -c 4 " . $_GET['host']);Secure Implementation
Python (subprocess with list arguments):
import subprocess
import shlex
import re
def safe_ping(host):
# Validate input
if not re.match(r'^[a-zA-Z0-9.-]+$', host):
raise ValueError("Invalid hostname")
# Use list form (no shell interpretation)
result = subprocess.run(
['ping', '-c', '4', host],
capture_output=True,
text=True,
timeout=30
)
return result.stdoutNode.js (execFile with array arguments):
const { execFile } = require('child_process');
function safePing(host, callback) {
// Validate input
if (!/^[a-zA-Z0-9.-]+$/.test(host)) {
return callback(new Error('Invalid hostname'));
}
execFile('ping', ['-c', '4', host], (error, stdout, stderr) => {
callback(error, stdout);
});
}PHP (escapeshellarg):
$host = escapeshellarg($_GET['host']);
$output = shell_exec("ping -c 4 " . $host);Best Practice
๐งช Testing Verification
After implementing fixes, verify they work with these test payloads:
SQL Injection Test Payloads
' OR '1'='1 ' OR 1=1-- 1; DROP TABLE users-- Query should fail or return no results
No SQL errors exposed to user
Application logs show bound parameters
Command Injection Test Payloads
; ls -la | cat /etc/passwd $(whoami) Command not executed
Input rejected or sanitized
Only expected output returned
# Automated testing with SQLMap (verify fix blocks injection)
sqlmap -u "https://example.com/api/user?id=1" --batch --level=3
# If properly fixed, SQLMap should report:
# "all tested parameters do not appear to be injectable"Common Mistakes
Blacklist Filtering
Trying to block specific keywords like "SELECT", "DROP", "UNION" is easily bypassed with encoding, case variations, or alternative syntax.
SeLeCt, %53%45%4C%45%43%54, /*!SELECT*/ Escaping Instead of Parameterization
Using escape functions like mysql_real_escape_string() or manual quote escaping is error-prone and doesn't protect against all injection vectors. Always use parameterized queries.
Parameterizing Only Some Inputs
Fixing user-facing inputs but leaving admin panels, internal APIs, or batch jobs vulnerable. ALL user-controlled data must use parameterized queries.
Using String Interpolation with ORMs
Even with ORMs, using raw queries with string interpolation reintroduces vulnerabilities.