Introduction
Secure coding practices protect applications from common vulnerabilities. This tutorial covers input validation, SQL injection prevention, and other essential security measures.
Input Validation
import re
from typing import Any, List
from pydantic import BaseModel, validator, Field
class UserInput(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: str
age: int = Field(..., ge=0, le=150)
password: str = Field(..., min_length=8)
@validator('username')
def validate_username(cls, v):
if not re.match(r'^[a-zA-Z0-9_]+$', v):
raise ValueError('Username can only contain letters, numbers, and underscores')
return v
@validator('email')
def validate_email(cls, v):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, v):
raise ValueError('Invalid email format')
return v
def validate_input(value: str, pattern: str) -> bool:
"""Validate input against regex pattern."""
return bool(re.match(pattern, value))
def sanitize_input(text: str) -> str:
"""Remove potentially dangerous characters."""
return re.sub(r'[<>\"\'%;()&+]', '', text)
SQL Injection Prevention
# Using parameterized queries - SAFE
def get_user_by_id(user_id: int):
query = "SELECT * FROM users WHERE id = %s"
cursor.execute(query, (user_id,))
# Using ORM - SAFE
def get_user_by_username(username: str):
return User.query.filter_by(username=username).first()
# Dynamic query construction - DANGEROUS
def get_user_dangerous(username: str):
query = f"SELECT * FROM users WHERE username = '{username}'" # NEVER DO THIS
return cursor.execute(query)
# Using SQLAlchemy with proper escaping
from sqlalchemy import text
def search_users(search_term: str):
query = text("SELECT * FROM users WHERE username LIKE :search")
result = session.execute(query, {"search": f"%{search_term}%"})
return result.fetchall()
Command Injection Prevention
import subprocess
import shlex
# Using list arguments - SAFE
def list_files_safe(directory: str):
result = subprocess.run(
['ls', '-la', directory],
capture_output=True,
text=True,
shell=False
)
return result.stdout
# Using shell=True - DANGEROUS
def list_files_dangerous(directory: str):
result = subprocess.run(
f'ls -la {directory}', # NEVER DO THIS
shell=True,
capture_output=True
)
return result.stdout
# Using shlex for shell commands
def safe_shell_command(cmd: str, args: List[str]):
escaped_args = [shlex.quote(arg) for arg in args]
full_cmd = f"{cmd} {' '.join(escaped_args)}"
return subprocess.run(full_cmd, shell=True, capture_output=True)
Path Traversal Prevention
import os
from pathlib import Path
def safe_read_file(base_dir: str, filename: str):
"""Safely read a file preventing path traversal."""
base_path = Path(base_dir).resolve()
target_path = (base_path / filename).resolve()
# Check if resolved path is under base directory
if not target_path.is_relative_to(base_path):
raise ValueError("Invalid file path")
return target_path.read_text()
def validate_filename(filename: str) -> bool:
"""Validate filename to prevent path traversal."""
dangerous_patterns = ['..', '~', '/', '\\', '\x00']
return not any(pattern in filename for pattern in dangerous_patterns)
Practice Problems
- Implement a comprehensive input validation middleware
- Create a safe file upload handler with type checking
- Add CSRF token validation to forms
- Implement rate limiting for API endpoints
- Create a security header middleware