OWASP Security Checklist
A hands-on implementation guide for the OWASP Top 10 (2021) that goes far beyond listing vulnerabilities — it provides working Python code examples demonstrating each vulnerability, automated testing scripts to detect them in your codebase, and production-ready remediation patterns you can copy directly into your projects. Each vulnerability category includes a detection script, a vulnerable code sample, the secure alternative, and a validation test.
Key Features
- All 10 OWASP Categories Covered — A01:Broken Access Control through A10:SSRF, each with dedicated detection and remediation modules.
- Vulnerable vs. Secure Code Pairs — Side-by-side Python examples showing the insecure pattern and the hardened version for every vulnerability class.
- Automated Scanner Scripts — Python scripts that scan your source code for common vulnerability patterns (SQL concatenation, hardcoded secrets, missing auth checks, open redirects).
- Remediation Patterns Library — Copy-paste secure implementations: parameterized queries, CSRF tokens, output encoding, secure session management, and more.
- Testing Scripts — Automated tests that verify your application is not vulnerable to each OWASP category.
- Decision Matrix — Risk-priority scoring to determine which vulnerabilities to fix first based on exploitability and business impact.
- CI/CD Integration Guide — Instructions for running OWASP checks as part of your build pipeline with pass/fail gates.
Quick Start
# Extract the checklist
unzip owasp-security-checklist.zip
cd owasp-security-checklist/
# Scan your Python project for OWASP vulnerabilities
python3 scripts/security_scan.py --target ./your_project/ --output owasp_report.json
# Run the interactive checklist
python3 scripts/checklist_runner.py --format markdown --output checklist_status.md
A03: Injection — Detection Example
import logging
from pathlib import Path
logger = logging.getLogger(__name__)
class InjectionScanner:
"""Detect potential SQL injection vulnerabilities in Python source files."""
DANGEROUS_PATTERNS = [
"execute(f\"", "execute(\"", ".format(", "% (", "cursor.execute(query)",
]
def __init__(self, project_path: str):
self.project_path = Path(project_path)
self.findings: list[dict[str, str | int]] = []
def scan(self) -> list[dict[str, str | int]]:
"""Scan all Python files for injection patterns."""
for py_file in self.project_path.rglob("*.py"):
content = py_file.read_text(encoding="utf-8", errors="ignore")
for line_num, line in enumerate(content.splitlines(), 1):
for pattern in self.DANGEROUS_PATTERNS:
if pattern in line:
self.findings.append({
"file": str(py_file), "line": line_num,
"pattern": pattern, "severity": "HIGH",
})
logger.info("Injection scan complete: %d findings", len(self.findings))
return self.findings
Architecture / How It Works
┌────────────────────────────────────────────────┐
│ OWASP Scan Pipeline │
│ │
│ Source Code ──► Static Analyzers ──► Findings │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ A01: Access A03: Injection Risk Matrix │
│ A02: Crypto A07: XSS (prioritize) │
│ A05: Misconfig A09: Logging │
│ A10: SSRF ... │
│ │
│ Findings ──► Remediation Patterns ──► Tests │
│ │ │ │
│ ▼ ▼ │
│ Secure Code Validation │
│ (copy-paste) (pass/fail) │
└────────────────────────────────────────────────┘
Usage Examples
A01: Broken Access Control — Secure Pattern
import functools
import logging
from typing import Callable, Any
logger = logging.getLogger(__name__)
def require_role(*allowed_roles: str) -> Callable:
"""Decorator enforcing role-based access control on route handlers."""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(request: Any, *args: Any, **kwargs: Any) -> Any:
user_role = getattr(request, "user_role", None)
if user_role not in allowed_roles:
logger.warning(
"Access denied: user=%s role=%s required=%s endpoint=%s",
getattr(request, "user_id", "unknown"),
user_role,
allowed_roles,
func.__name__,
)
raise PermissionError(f"Role '{user_role}' not authorized")
return func(request, *args, **kwargs)
return wrapper
return decorator
# Usage:
# @require_role("admin", "security_lead")
# def delete_user(request, user_id): ...
A03: Injection — Vulnerable vs. Secure
# VULNERABLE — string concatenation in SQL
def get_user_bad(cursor, username: str):
cursor.execute(f"SELECT * FROM users WHERE name = '{username}'") # SQL injection!
return cursor.fetchone()
# SECURE — parameterized query
def get_user_safe(cursor, username: str):
cursor.execute("SELECT * FROM users WHERE name = ?", (username,))
return cursor.fetchone()
# A07: XSS — Always encode output for context
import html
def render_comment(comment: str) -> str:
return f'<div class="comment">{html.escape(comment, quote=True)}</div>'
# Input: <script>alert('xss')</script>
# Output: <script>alert('xss')</script>
Configuration
| Parameter | Default | Description |
|---|---|---|
scan.target_extensions |
[".py", ".js", ".ts"] |
File types to scan |
scan.exclude_dirs |
["venv", "node_modules", ".git"] |
Directories to skip |
scan.severity_threshold |
MEDIUM |
Minimum severity to report |
report.format |
json |
Output format: json, markdown, html
|
report.include_code |
true |
Include source code snippets in findings |
ci.fail_on_high |
true |
Fail CI pipeline on HIGH severity findings |
ci.fail_on_medium |
false |
Fail CI pipeline on MEDIUM severity findings |
Best Practices
- Fix injections first — A03 (Injection) has the highest exploitability and impact. Parameterize every database query without exception.
- Validate on the server, always — Client-side validation is for UX. Server-side validation is for security. Never trust input from any source.
- Use allowlists over denylists — Define what IS allowed, not what is forbidden. Attackers will always find patterns you didn't block.
- Encode output for the context — HTML encoding for HTML, URL encoding for URLs, JavaScript encoding for JS. Context matters.
- Log security events — Failed authentication, access control violations, and input validation failures should all generate security logs.
- Automate in CI/CD — Run the scanner on every pull request. Security debt compounds faster than technical debt.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Scanner reports false positives on ORM code | ORM handles parameterization internally | Add ORM patterns to scan.safe_patterns allowlist |
| Report is empty despite known issues | Wrong target_extensions or excluded dirs |
Check config; verify target path contains scannable files |
| CI pipeline failing on test dependencies | Scanner tries to import scanned code | Use --static-only flag to skip import-based analysis |
| XSS findings in API-only endpoints | Scanner doesn't distinguish API from HTML responses | Tag endpoints with @api_only and add to scan.exclude_annotations
|
This is 1 of 9 resources in the Security Engineer Pro toolkit. Get the complete [OWASP Security Checklist] with all files, templates, and documentation for $29.
Or grab the entire Security Engineer Pro bundle (9 products) for $119 — save 30%.
Top comments (0)