6.7 KiB
Injection Prevention Reference
Overview
Injection flaws occur when untrusted data is sent to an interpreter as part of a command or query. The attacker's hostile data tricks the interpreter into executing unintended commands or accessing data without proper authorization.
SQL Injection
Primary Defenses
1. Prepared Statements (Parameterized Queries) - REQUIRED
The database distinguishes between code and data regardless of user input.
// SAFE: Parameterized query
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, userInput);
# SAFE: Parameterized query
cursor.execute("SELECT * FROM users WHERE username = %s", (user_input,))
// SAFE: Parameterized query (node-postgres)
const result = await client.query('SELECT * FROM users WHERE id = $1', [userId]);
2. Stored Procedures
Safe when implemented without dynamic SQL construction.
// SAFE: Stored procedure
CallableStatement cs = connection.prepareCall("{call sp_getUser(?)}");
cs.setString(1, username);
3. Allow-list Input Validation
For elements that cannot be parameterized (table names, column names, sort order).
// SAFE: Allowlist for table names
switch(tableName) {
case "users": return "users";
case "orders": return "orders";
default: throw new InputValidationException("Invalid table");
}
Vulnerable Patterns to Find
# VULNERABLE: String concatenation
query = "SELECT * FROM users WHERE name = '" + user_input + "'"
# VULNERABLE: f-string interpolation
query = f"SELECT * FROM users WHERE id = {user_id}"
# VULNERABLE: format() method
query = "SELECT * FROM users WHERE name = '{}'".format(user_input)
// VULNERABLE: Template literal
const query = `SELECT * FROM users WHERE id = ${userId}`;
// VULNERABLE: String concatenation
const query = "SELECT * FROM users WHERE name = '" + userName + "'";
ORM Safety Considerations
Django ORM
# SAFE: ORM methods
User.objects.filter(username=user_input)
# VULNERABLE: raw() with interpolation
User.objects.raw(f"SELECT * FROM users WHERE name = '{user_input}'")
# VULNERABLE: extra() with unvalidated input
User.objects.extra(where=[f"name = '{user_input}'"])
SQLAlchemy
# SAFE: ORM methods
session.query(User).filter(User.name == user_input)
# VULNERABLE: text() with interpolation
session.execute(text(f"SELECT * FROM users WHERE name = '{user_input}'"))
NoSQL Injection
MongoDB Injection Patterns
// VULNERABLE: User-controlled query operators
db.users.find({ username: req.body.username, password: req.body.password });
// Attack: { "username": "admin", "password": { "$gt": "" } }
// SAFE: Explicit type checking
const username = String(req.body.username);
const password = String(req.body.password);
db.users.find({ username: username, password: password });
Dangerous Operators
$where- Allows JavaScript execution$regex- Can be used for ReDoS$gt,$ne,$in- Query manipulation when user-controlled
OS Command Injection
Primary Defenses
1. Avoid Shell Commands - PREFERRED
Use language built-in functions instead of shell commands.
# VULNERABLE: Shell command
os.system(f"mkdir {directory_name}")
# SAFE: Built-in function
os.makedirs(directory_name, exist_ok=True)
2. Parameterization
# VULNERABLE: Shell=True with user input
subprocess.run(f"convert {input_file} {output_file}", shell=True)
# SAFE: List of arguments, shell=False
subprocess.run(["convert", input_file, output_file], shell=False)
3. Input Validation
# Allowlist for permitted commands
ALLOWED_COMMANDS = {"convert", "resize", "rotate"}
if command not in ALLOWED_COMMANDS:
raise ValueError("Invalid command")
# Validate arguments against safe patterns
if not re.match(r'^[a-zA-Z0-9_\-\.]+$', filename):
raise ValueError("Invalid filename")
Dangerous Characters
Block or escape: & | ; $ > < \ ! ' " ( ) { } [ ] \n \r
Language-Specific Dangerous Functions
| Language | Dangerous Functions |
|---|---|
| Python | os.system(), subprocess.run(shell=True), os.popen(), eval(), exec() |
| JavaScript | child_process.exec(), eval() |
| PHP | exec(), shell_exec(), system(), passthru(), backticks |
| Ruby | system(), exec(), backticks, %x{} |
| Java | Runtime.exec(), ProcessBuilder with shell |
LDAP Injection
Prevention
// SAFE: Escape special characters
String safeName = LdapEncoder.filterEncode(userName);
String filter = "(&(uid=" + safeName + ")(userPassword=" + safePassword + "))";
Characters to Escape in LDAP
- Filter context:
* ( ) \ NUL - DN context:
\ # + < > ; " = /
Template Injection
Server-Side Template Injection (SSTI)
# VULNERABLE: User input in template
template = Template(f"Hello {user_input}")
# SAFE: Pass user input as variable
template = Template("Hello {{ name }}")
template.render(name=user_input)
Detection Payloads
- Jinja2:
{{7*7}}→49 - FreeMarker:
${7*7}→49 - Thymeleaf:
[[${7*7}]]→49
XPath Injection
Prevention
// VULNERABLE: String concatenation
String query = "//users/user[name='" + userName + "']";
// SAFE: Use parameterized XPath
XPathExpression expr = xpath.compile("//users/user[name=$name]");
expr.setVariable("name", userName);
Key Grep Patterns for Detection
# SQL Injection
grep -rn "execute.*+" --include="*.py"
grep -rn "raw_sql\|rawQuery\|raw(" --include="*.py" --include="*.js"
grep -rn "\\.query\\(.*\\+" --include="*.js"
grep -rn "\\$.*\\+" --include="*.php"
# Command Injection
grep -rn "os\\.system\\|subprocess\\.run.*shell=True\\|os\\.popen" --include="*.py"
grep -rn "child_process\\.exec" --include="*.js"
grep -rn "system(\\|exec(\\|shell_exec(" --include="*.php"
# Template Injection
grep -rn "Template(.*\\+" --include="*.py"
grep -rn "render_template_string" --include="*.py"
# LDAP Injection
grep -rn "ldap_search\\|ldap_bind" --include="*.py" --include="*.php"