647928a0a1
Fundamentals: secure-defaults, input-validation, credential-handling, audit-logging Identity: authentication, authorization Attack Prevention: injection-prevention, dos-prevention, prompt-injection
3.0 KiB
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)