Files
security-patterns/injection-prevention.md
Rodin 647928a0a1 Initial commit: 9 security patterns for code review
Fundamentals: secure-defaults, input-validation, credential-handling, audit-logging
Identity: authentication, authorization
Attack Prevention: injection-prevention, dos-prevention, prompt-injection
2026-05-10 22:45:03 -07:00

3.0 KiB

Injection Prevention

Rule

Never concatenate untrusted input into commands, queries, or templates. Use parameterized APIs.

Source: OWASP Injection

SQL Injection

Correct Pattern

# Parameterized query — safe
def get_user(user_id: int):
    cursor.execute(
        "SELECT * FROM users WHERE id = %s",
        (user_id,)
    )
    return cursor.fetchone()

# ORM — safe
def get_user(user_id: int):
    return User.query.filter_by(id=user_id).first()

Incorrect Pattern

# Wrong: string concatenation
def get_user(user_id):
    cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
    # Input: "1; DROP TABLE users; --"

# Wrong: string formatting
query = "SELECT * FROM users WHERE name = '%s'" % name

Command Injection

Correct Pattern

import subprocess
import shlex

# Use list form — shell=False prevents injection
def run_command(filename: str):
    result = subprocess.run(
        ["ls", "-la", filename],
        capture_output=True,
        shell=False  # Critical!
    )
    return result.stdout

# If you must use shell, validate strictly
VALID_FILENAME = re.compile(r'^[a-zA-Z0-9._-]+$')

def safe_filename(name: str) -> str:
    if not VALID_FILENAME.match(name):
        raise ValueError("Invalid filename")
    return name

Incorrect Pattern

# Wrong: shell=True with user input
subprocess.run(f"ls -la {filename}", shell=True)
# Input: "file.txt; rm -rf /"

# Wrong: os.system
os.system(f"convert {input_file} {output_file}")

Template Injection

Correct Pattern

# Use auto-escaping templates
from jinja2 import Environment, select_autoescape

env = Environment(autoescape=select_autoescape(['html', 'xml']))
template = env.get_template("page.html")
output = template.render(user_name=user_input)  # Auto-escaped

Incorrect Pattern

# Wrong: rendering user input as template
template = Template(user_input)  # SSTI vulnerability

# Wrong: disabling auto-escape
template.render(content=Markup(user_input))

Path Traversal

Correct Pattern

import os
from pathlib import Path

UPLOAD_DIR = Path("/app/uploads").resolve()

def safe_path(filename: str) -> Path:
    """Ensure path stays within allowed directory."""
    # Resolve to absolute, normalized path
    requested = (UPLOAD_DIR / filename).resolve()
    
    # Verify it's still under UPLOAD_DIR
    if not requested.is_relative_to(UPLOAD_DIR):
        raise ValueError("Path traversal detected")
    
    return requested

Incorrect Pattern

# Wrong: direct concatenation
path = f"/app/uploads/{filename}"
# Input: "../../../etc/passwd"

# Wrong: checking for ".." without resolving
if ".." not in filename:  # Can bypass with encoding
    open(f"/uploads/{filename}")

Edge Cases

  • Second-order injection (stored, then executed later)
  • Polyglot payloads (valid in multiple contexts)
  • Encoding bypasses (URL, Unicode, hex)
  • Blind injection (no visible output)