OWASP Top 10
Protect against the most critical web security risks.
- Broken Access Control
❌ Bad: No authorization check
@app.route('/api/users/<user_id>') def get_user(user_id): return db.query(f"SELECT * FROM users WHERE id = {user_id}")
✅ Good: Verify user can access resource
@app.route('/api/users/<user_id>') @login_required def get_user(user_id): if current_user.id != user_id and not current_user.is_admin: abort(403) return db.query("SELECT * FROM users WHERE id = ?", [user_id])
- Cryptographic Failures
❌ Bad: Weak hashing
import hashlib password_hash = hashlib.md5(password.encode()).hexdigest()
✅ Good: Strong hashing
from argon2 import PasswordHasher ph = PasswordHasher() password_hash = ph.hash(password)
- Injection
❌ Bad: SQL injection vulnerable
query = f"SELECT * FROM users WHERE email = '{email}'"
✅ Good: Parameterized query
query = "SELECT * FROM users WHERE email = ?" db.execute(query, [email])
- Insecure Design
-
No rate limiting on login
-
Sequential/guessable IDs
-
No CAPTCHA on sensitive operations
Fix: Use UUIDs, implement rate limiting, threat model early.
- Security Misconfiguration
❌ Bad: Debug mode in production
app.debug = True
✅ Good: Environment-based config
app.debug = os.getenv('FLASK_ENV') == 'development'
- Vulnerable Components
Scan for vulnerabilities
npm audit pip-audit
Fix vulnerabilities
npm audit fix
- Authentication Failures
✅ Strong password requirements
def validate_password(password): if len(password) < 12: return "Password must be 12+ characters" if not re.search(r"[A-Z]", password): return "Must contain uppercase" if not re.search(r"[0-9]", password): return "Must contain number" return None
JWT Security (OWASP Best Practices)
import jwt import hashlib import secrets from datetime import datetime, timezone, timedelta
❌ Bad: Trust algorithm from header
payload = jwt.decode(token, SECRET, algorithms=jwt.get_unverified_header(token)['alg'])
✅ Good: Hardcode expected algorithm (prevents algorithm confusion attacks)
def verify_jwt(token: str) -> dict: try: payload = jwt.decode( token, SECRET_KEY, algorithms=['HS256'], # NEVER read from header options={ 'require': ['exp', 'iat', 'iss', 'aud'], # Required claims } )
# Validate issuer and audience
if payload['iss'] != EXPECTED_ISSUER:
raise jwt.InvalidIssuerError()
if payload['aud'] != EXPECTED_AUDIENCE:
raise jwt.InvalidAudienceError()
return payload
except jwt.ExpiredSignatureError:
raise AuthError("Token expired")
except jwt.InvalidTokenError as e:
raise AuthError(f"Invalid token: {e}")
Token sidejacking protection (OWASP recommended)
def create_protected_token(user_id: str, response) -> str: """Create token with user context to prevent sidejacking.""" # Generate random fingerprint fingerprint = secrets.token_urlsafe(32)
# Store fingerprint hash in token (not raw value)
payload = {
'user_id': user_id,
'fingerprint': hashlib.sha256(fingerprint.encode()).hexdigest(),
'exp': datetime.now(timezone.utc) + timedelta(minutes=15),
'iat': datetime.now(timezone.utc),
'iss': ISSUER,
'aud': AUDIENCE,
}
# Send raw fingerprint as hardened cookie
response.set_cookie(
'__Secure-Fgp', # Cookie prefix for extra security
fingerprint,
httponly=True,
secure=True,
samesite='Strict',
max_age=900 # 15 min
)
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
JWT Security Checklist:
-
Hardcode algorithm (never read from header)
-
Validate: exp, iat, iss, aud claims
-
Short expiry (15 min - 1 hour)
-
Use refresh token rotation for longer sessions
-
Implement token denylist for logout/revocation
- Data Integrity Failures
<!-- Use SRI for CDN scripts --> <script src="https://cdn.example.com/lib.js" integrity="sha384-..." crossorigin="anonymous"></script>
- Logging Failures
✅ Log security events
@app.route('/login', methods=['POST']) def login(): user = authenticate(email, password) if user: logger.info(f"Successful login: {email}") else: logger.warning(f"Failed login: {email}")
- SSRF (Server-Side Request Forgery)
❌ Bad: Fetch any URL
response = requests.get(user_provided_url)
✅ Good: Allowlist domains
ALLOWED = ['api.example.com'] if urlparse(url).hostname not in ALLOWED: abort(400)
Quick Checklist
-
Authorization on all endpoints
-
Passwords hashed with bcrypt/argon2
-
Parameterized queries only
-
Rate limiting enabled
-
Debug mode off in production
-
Dependencies scanned regularly
-
Security events logged
Related Skills
-
auth-patterns
-
Authentication implementation
-
input-validation
-
Sanitization patterns
-
security-scanning
-
Automated scanning
Capability Details
injection
Keywords: sql injection, command injection, injection, parameterized Solves:
-
Prevent SQL injection
-
Fix command injection
-
Use parameterized queries
access-control
Keywords: access control, authorization, idor, privilege Solves:
-
Fix broken access control
-
Prevent IDOR vulnerabilities
-
Implement authorization checks
owasp-fixes
Keywords: fix, mitigation, example, vulnerability Solves:
-
OWASP vulnerability fixes
-
Mitigation examples
-
Code fix patterns