Chuyển đến nội dung chính

Lesson 13: OWASP Top 10 (2025) — Detailed Analysis

OWASP Top 10 latest versions, each category with actual exploitation demo and prevention methods.

🔒 DevSecOps — Lesson 13 Lesson 13: OWASP Top 10 (2025) — Analysis Details

Performance Testing & Pentest: Enterprise Standard Process 2026

Part 3: Pentest Foundations — Process and Methodology

xdev.asia

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.