1. OWASP Top 10 — Overview
OWASP Top 10 is a list of the 10 most common web application security risks, updated based on real-world data from thousands of organizations globally.
OWASP Top 10 (2025 Edition):
# │ Category │ Severity
────┼────────────────────────────────────────┼──────────
A01│ Broken Access Control │ Critical
A02│ Cryptographic Failures │ High
A03│ Injection │ Critical
A04│ Insecure Design │ High
A05│ Security Misconfiguration │ High
A06│ Vulnerable & Outdated Components │ High
A07│ Identification & Authentication Fail │ Critical
A08│ Software & Data Integrity Failures │ High
A09│ Security Logging & Monitoring Failures │ Medium
A10│ Server-Side Request Forgery (SSRF) │ High
2. A01: Broken Access Control
Loại tấn công phổ biến:
├── IDOR (Insecure Direct Object Reference)
├── Privilege Escalation (vertical/horizontal)
├── Missing Function Level Access Control
├── CORS misconfiguration
└── JWT manipulation
# IDOR Example — Vulnerable code
@app.get("/api/users/{user_id}/profile")
def get_profile(user_id: int):
# ❌ No authorization check — any user can view any profile
return db.query(User).filter(User.id == user_id).first()
# ✅ Fixed — Check authorization
@app.get("/api/users/{user_id}/profile")
def get_profile(user_id: int, current_user: User = Depends(get_current_user)):
if current_user.id != user_id and not current_user.is_admin:
raise HTTPException(status_code=403, detail="Forbidden")
return db.query(User).filter(User.id == user_id).first()
# Testing IDOR with curl
# Login as user A, get token
TOKEN_A="eyJhbGc..."
# Try to access user B's data with user A's token
curl -H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/api/users/999/profile
# If 200 OK → IDOR vulnerability confirmed
3. A02: Cryptographic Failures
Common failures:
├── Hardcoded secrets in source code
├── Weak algorithms (MD5, SHA1, DES)
├── Missing TLS / using TLS 1.0-1.1
├── Sensitive data in logs
└── Weak key generation
# ❌ Vulnerable — MD5 for password hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()
# ✅ Secure — bcrypt with cost factor
import bcrypt
password_hash = bcrypt.hashpw(
password.encode('utf-8'),
bcrypt.gensalt(rounds=12)
)
# Test TLS configuration
# testssl.sh — comprehensive TLS scanner
./testssl.sh --severity HIGH https://example.com
# Check certificate
openssl s_client -connect example.com:443 2>/dev/null | \
openssl x509 -noout -dates -subject -issuer
# Scan for weak ciphers
nmap --script ssl-enum-ciphers -p 443 example.com
4. A03: Injection
Injection Types:
├── SQL Injection (SQLi)
├── NoSQL Injection
├── OS Command Injection
├── LDAP Injection
├── Template Injection (SSTI)
└── Expression Language Injection
# ❌ SQL Injection — Vulnerable
@app.get("/search")
def search(q: str):
query = f"SELECT * FROM products WHERE name LIKE '%{q}%'"
return db.execute(text(query)).fetchall()
# Input: ' OR '1'='1' --
# Result: Returns ALL products
# ✅ Parameterized query — Secure
@app.get("/search")
def search(q: str):
query = text("SELECT * FROM products WHERE name LIKE :search")
return db.execute(query, {"search": f"%{q}%"}).fetchall()
# SQLMap — Automated SQL Injection testing
sqlmap -u "https://example.com/search?q=test" \
--batch --level=3 --risk=2 \
--dbs --tables \
--output-dir=./sqlmap_results
# Manual testing payloads
# ' OR 1=1 --
# ' UNION SELECT null,null,null --
# '; DROP TABLE users; --
# ' AND SLEEP(5) -- (time-based blind)
5. A04: Insecure Design
Insecure Design Examples:
├── Missing rate limiting on password reset
├── Security questions as only recovery method
├── No re-authentication for sensitive actions
├── Predictable resource locations
└── Missing business logic validation
Secure Design Principles:
├── Defense in Depth
├── Least Privilege
├── Fail Secure (not fail open)
├── Separation of Duties
├── Zero Trust Architecture
└── Threat Modeling from design phase
# ❌ Insecure Design — No rate limiting on OTP
@app.post("/verify-otp")
def verify_otp(phone: str, otp: str):
# Attacker can brute-force 4-digit OTP (0000-9999)
if db.get_otp(phone) == otp:
return {"status": "verified"}
# ✅ Secure Design — Rate limit + lockout + expiry
@app.post("/verify-otp")
@rate_limit(max_attempts=5, window_seconds=300)
def verify_otp(phone: str, otp: str, request: Request):
stored = db.get_otp(phone)
if not stored or stored.expired:
raise HTTPException(400, "OTP expired")
if stored.attempts >= 5:
db.invalidate_otp(phone)
raise HTTPException(429, "Too many attempts")
if not hmac.compare_digest(stored.code, otp):
db.increment_otp_attempts(phone)
raise HTTPException(400, "Invalid OTP")
db.invalidate_otp(phone)
return {"status": "verified"}
6. A05: Security Misconfiguration
# Common misconfigurations to check
# 1. Default credentials
hydra -l admin -P /usr/share/wordlists/common.txt \
https://example.com/admin http-post-form \
"/login:user=^USER^&pass=^PASS^:Invalid"
# 2. Directory listing
curl -s https://example.com/ | grep "Index of"
# 3. Unnecessary HTTP methods
curl -X OPTIONS https://example.com -i
# Check for PUT, DELETE, TRACE
# 4. Debug mode in production
curl https://example.com/debug
curl https://example.com/actuator/env # Spring Boot
curl https://example.com/phpinfo.php
# 5. Security headers check
curl -sI https://example.com | grep -iE \
"strict-transport|x-frame|x-content-type|content-security|referrer-policy"
# Expected headers:
# Strict-Transport-Security: max-age=31536000; includeSubDomains
# X-Frame-Options: DENY
# X-Content-Type-Options: nosniff
# Content-Security-Policy: default-src 'self'
# Referrer-Policy: strict-origin-when-cross-origin
7. A06-A07: Components & Authentication
# A06: Vulnerable Components
# Check npm dependencies
npm audit --audit-level=high
npx better-npm-audit audit
# Check Python dependencies
pip-audit
safety check
# Scan container images
trivy image myapp:latest --severity HIGH,CRITICAL
# Dependency-Track — SBOM analysis
# Upload SBOM (CycloneDX format)
cyclonedx-npm --output-format json > sbom.json
A07: Authentication Failures:
├── Credential stuffing (leaked password databases)
├── Brute force (no lockout/rate limiting)
├── Default credentials
├── Weak password policy
├── Session fixation
├── Missing MFA on critical functions
└── JWT implementation flaws
├── Algorithm confusion (none, HS256 vs RS256)
├── Missing expiration
└── Weak signing key
# JWT Algorithm Confusion Attack
import jwt
# ❌ Vulnerable — accepts 'none' algorithm
token = jwt.decode(token_string, options={"verify_signature": False})
# ✅ Secure — explicitly specify algorithm
token = jwt.decode(
token_string,
key=PUBLIC_KEY,
algorithms=["RS256"], # Whitelist allowed algorithms
options={"require": ["exp", "iss", "sub"]}
)
8. A08-A10: Integrity, Logging, SSRF
A08: Software & Data Integrity Failures:
├── Insecure deserialization
├── CI/CD pipeline compromise
├── Unsigned software updates
├── Supply chain attacks
└── Integrity verification missing
A09: Security Logging & Monitoring:
├── Missing audit logs for auth events
├── Logs not centralized
├── No alerting on suspicious activity
├── Log injection vulnerabilities
└── Insufficient log retention
# A10: SSRF — Server-Side Request Forgery
# ❌ Vulnerable — user controls URL
@app.post("/fetch-url")
def fetch_url(url: str):
response = requests.get(url) # Attacker: url=http://169.254.169.254/latest/meta-data/
return response.text
# ✅ Secure — URL validation + allowlist
import ipaddress
from urllib.parse import urlparse
ALLOWED_DOMAINS = {"api.trusted.com", "cdn.example.com"}
@app.post("/fetch-url")
def fetch_url(url: str):
parsed = urlparse(url)
# Block internal IPs
try:
ip = ipaddress.ip_address(parsed.hostname)
if ip.is_private or ip.is_loopback or ip.is_link_local:
raise HTTPException(400, "Internal addresses blocked")
except ValueError:
pass # hostname, not IP
# Domain allowlist
if parsed.hostname not in ALLOWED_DOMAINS:
raise HTTPException(400, "Domain not allowed")
if parsed.scheme not in ("https",):
raise HTTPException(400, "Only HTTPS allowed")
response = requests.get(url, timeout=5, allow_redirects=False)
return response.text
9. OWASP Testing Checklist
Pentest Checklist theo OWASP:
□ A01: Test IDOR on all API endpoints
□ A01: Test horizontal & vertical privilege escalation
□ A01: Test CORS configuration
□ A02: Check TLS version and cipher suites
□ A02: Scan for hardcoded secrets (git-secrets, trufflehog)
□ A03: Test all input fields for SQLi, XSS, Command injection
□ A04: Review business logic for design flaws
□ A05: Check security headers, error handling, debug endpoints
□ A06: Run dependency audit (npm audit, pip-audit, trivy)
□ A07: Test authentication bypass, session management
□ A08: Check CI/CD pipeline security, dependency integrity
□ A09: Verify logging of security events
□ A10: Test SSRF on URL input parameters
10. Summary
- A01 Broken Access Control: #1 risk — test IDOR privilege, escalation
- A03 Injection: Parameterized queries, input validation
- A05 Misconfiguration: Security headers, debug endpoints, defaults
- A07 Authentication: MFA, rate limiting, JWT security
- A10 SSRF: URL validation, IP blocking, domain allowlist
The next lesson will practice Reconnaissance and Scanning with Nmap, Amass, Nuclei.