Implement access control as a framework-level middleware, not per-route. Every route should require Authentication by default, with explicit opt-in for public routes. This prevents the most common Access control bug: forgetting to add @require_auth to a new route.
# VULNERABLE: MD5 for passwords
password_hash = hashlib.md5(password.encode()).hexdigest()
# VULNERABLE: SHA-1 for signatures
signature = hashlib.sha1(data.encode()).hexdigest()
# SAFE: bcrypt for passwords
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# SAFE: SHA-256 for signatures
signature = hashlib.sha256(data.encode()).hexdigest()
# SAFE: Use a proven library (e.g., argon2id)
from argon2 import PasswordHasher
password_hash = ph.hash(password)
// VULNERABLE: API keys in client-side code
const API_KEY = 'sk-1234567890abcdef';
fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${API_KEY}` },
// SAFE: API keys on server side only
// Client sends request to your backend, backend uses the key
# VULNERABLE: no TLS, or allowing HTTP
# Sensitive data sent in cleartext
# SAFE: redirect all HTTP to HTTPS, use HSTS
return 301 https://$host$request_uri;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
| Method | Approach |
|---|
| Credential scanning | TruffleHog, git-secrets, detect-secrets |
| TLS configuration | SSL Labs, testssl.sh, nmap —script ssl-enum-ciphers |
| Password storage audit | Code review for hashing algorithm used |
| Transport security | Verify TLS 1.2+, HSTS, no mixed content |
# Classic (string concatenation)
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
cursor.execute(f"SELECT name FROM products WHERE id = {user_id} UNION SELECT password FROM users")
cursor.execute(f"SELECT * FROM users WHERE id = {user_id} AND SUBSTRING(password,1,1) = 'a'")
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}; IF SUBSTRING(password,1,1)='a' WAITFOR DELAY '0:0:5'")
# SAFE: parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# VULNERABLE: MongoDB injection via dictionary merging
@app.route('/login', methods=['POST'])
query = {"username": request.form['username'], "password": request.form['password']}
user = db.users.find_one(query) # attacker sends {"password": {"$ne": ""}}
# SAFE: use explicit field operators
@app.route('/login', methods=['POST'])
username = request.form['username']
password = request.form['password']
user = db.users.find_one({"username": username, "password": password})
# VULNERABLE: shell command with user input
filename = request.args.get('file')
os.system(f"convert {filename} output.png")
# SAFE: use subprocess with shell=False and argument list
filename = request.args.get('file')
subprocess.run(['convert', filename, 'output.png'], check=True)
# VULNERABLE: LDAP filter with user input
ldap_filter = f"(uid={username})"
# attacker: *)(uid=*))(|(uid=* → returns all entries
# SAFE: escape LDAP special characters
safe_username = ldap.filter.escape_filter_chars(username)
ldap_filter = f"(uid={safe_username})"
| Method | Tool / Approach |
|---|
| Automated scanning | SQLMap, OWASP ZAP, Burp Suite |
| SAST | Semgrep, CodeQL, Bandit (Python) |
| Parameterized queries | Use prepared statements for ALL database interactions |
| Input validation | Whitelist allowed characters/values |
Insecure design refers to fundamental flaws in the application’s architecture and design, as opposed To implementation bugs.
Common insecure design patterns:
1. No rate limiting on password reset endpoints
2. Account enumeration via different error messages ("user not found" vs "wrong password")
3. Missing business logic validation (negative quantities, oversized orders)
4. No separation between admin and user contexts
5. Trust boundaries not defined (client trusted with authorization decisions)
- Authentication required for all sensitive operations
- Authorization enforced at every level (feature, data, field)
- Input validation defined for every input
- Rate limiting on authentication and sensitive endpoints
- Audit logging for all state-changing operations
- Session management with appropriate timeouts
- Error handling that does not leak internal information
| Method | Approach |
|---|
| Threat modeling | STRIDE, DREAD, attack trees |
| Abuse cases | Define what an attacker SHOULD NOT be able to do |
| Security requirements | NIST SP 800-53, OWASP ASVS |
| Design review | Security-focused architecture review |
# Common default credentials to check and change:
# admin/admin, root/root, tomcat/tomcat, guest/guest
# Spring Boot Actuator: /actuator endpoints exposed by default
# JMX: jmxremote.password with default credentials
# Database: postgres/postgres, sa/ (SQL Server)
# Check for default credentials in your infrastructure
nmap --script default-credentials 10.0.0.0/24
# VULNERABLE: Spring Boot Actuator exposed without authentication
include: "*" # Exposes ALL actuator endpoints
# SAFE: expose only necessary endpoints with authentication
show-details: when-authorized
# VULNERABLE: stack traces in production responses
@app.errorhandler(Exception)
return jsonify({"error": str(e), "traceback": traceback.format_exc()}), 500
# SAFE: generic error in production, detailed in logs
@app.errorhandler(Exception)
app.logger.error(f"Unhandled exception: {traceback.format_exc()}")
return jsonify({"error": "Internal server error"}), 500
| Method | Approach |
|---|
| Configuration audit | CIS benchmarks, SCAP |
| Directory/file scanning | DirBuster, gobuster, ffuf |
| Header scanning | SecurityHeaders.com, Observatory |
| Automated compliance | InSpec, Open Policy Agent |
npm audit fix --force # breaks changes; review before using
safety check --full-report
mvn org.owasp:dependency-check-maven:check
1. Maintain a software bill of materials (SBOM)
2. Subscribe to CVE notifications for your dependencies
3. Automate scanning in CI/CD pipeline
4. Establish SLA for critical vulnerability remediation:
- Low: next release cycle
1. Pin dependency versions (lockfiles)
2. Verify package integrity (checksums, signatures)
3. Use private registries (Artifactory, Nexus)
4. Review new dependencies before adding
5. Use SLSA framework for build integrity
6. Monitor for typosquatting (npm audit, Snyk)
# Detection: many failed logins from different IPs using known breached credentials
# Prevention: rate limiting, account lockout, MFA
# Rate limiting middleware
from flask_limiter import Limiter
limiter = Limiter(app, key_func=lambda: request.remote_addr)
@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")
# Account lockout after N failed attempts
LOCKOUT_DURATION = 300 # 5 minutes
@app.route('/login', methods=['POST'])
attempts = redis.get(f"login_attempts:{ip}")
if attempts and int(attempts) >= FAILED_LOGIN_LIMIT:
return jsonify({"error": "Account temporarily locked"}), 429
# ... verify credentials ...
redis.incr(f"login_attempts:{ip}")
redis.expire(f"login_attempts:{ip}", LOCKOUT_DURATION)
return jsonify({"error": "Invalid credentials"}), 401
| Defense | Implementation |
|---|
| Rate limiting | Per-IP and per-account limits |
| Account lockout | Temporary lock after N failed attempts |
| CAPTCHA | After N failed attempts |
| MFA | TOTP, WebAuthn, push notification |
| Progressive delay | Exponential backoff on failed attempts |
| Breached password detection | Check against HaveIBeenPwned API |
# VULNERABLE: session ID not regenerated after login
@app.route('/login', methods=['POST'])
if valid_credentials(request.form):
session['user_id'] = user.id # session ID unchanged
return redirect('/dashboard')
# SAFE: regenerate session ID after authentication
@app.route('/login', methods=['POST'])
if valid_credentials(request.form):
session.clear() # clear old session
session.regenerate() # new session ID
session['user_id'] = user.id
return redirect('/dashboard')
# VULNERABLE: deserializing untrusted pickle data
data = pickle.loads(request.get_data()) # RCE
# VULNERABLE: deserializing untrusted YAML
data = yaml.load(request.get_data()) # RCE (yaml.load is unsafe)
# SAFE: use JSON for data-only deserialization
data = json.loads(request.get_data()) # no code execution
# SAFE: use yaml.safe_load
data = yaml.safe_load(request.get_data()) # no arbitrary objects
# Verify package signatures before installation
# npm: npm verify (limited support)
# Python: pip with --require-hashes in requirements.txt
# requirements.txt with pinned hashes
pip install --require-hashes -r requirements.txt
1. Use SRI (Subresource Integrity) for CDN-hosted scripts:
<script src="https://cdn.example.com/lib.js"
integrity="sha384-abc123..."
crossorigin="anonymous"></script>
2. Self-host critical dependencies
3. Monitor CDN availability and integrity
# SAFE: structured logging for security events
logger = structlog.get_logger()
@app.route('/login', methods=['POST'])
user_agent = request.headers.get('User-Agent', '')
if valid_credentials(request.form):
logger.info("login.success",
logger.warning("login.failure",
username=request.form.get('username', ''),
Minimum security events to log:
1. All authentication events (success and failure)
2. Authorization failures (access denied)
3. Data access events (read sensitive data)
4. Data modification events (create, update, delete)
5. System events (startup, shutdown, configuration changes)
6. Input validation failures
7. Rate limiting triggers
- Spike in failed authentication (brute force)
- Privilege escalation attempts
- Unusual data access patterns
- New admin account creation
- Certificate expiration approaching
- Unexpected geographic access
# VULNERABLE: server fetches arbitrary URLs
url = request.args.get('url')
return requests.get(url).text
# ?url=http://169.254.169.254/latest/meta-data/ (AWS metadata)
# SAFE: validate against allowlist
from urllib.parse import urlparse
ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']
url = request.args.get('url')
if parsed.hostname not in ALLOWED_DOMAINS:
return jsonify({"error": "Domain not allowed"}), 400
if parsed.scheme not in ('http', 'https'):
return jsonify({"error": "Scheme not allowed"}), 400
ip = ipaddress.ip_address(parsed.hostname)
if ip.is_private or ip.is_loopback:
return jsonify({"error": "Private IPs not allowed"}), 400
pass # hostname, checked against allowlist
return requests.get(url, timeout=5).text
# AWS IMDSv1 (vulnerable): no authentication required
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# AWS IMDSv2 (secure): requires session token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Mitigation: enable IMDSv2 (requires token)
# Also: use network policies to block metadata endpoint from application servers
# VULNERABLE: allows file:// protocol
requests.get(request.args.get('url'))
# ?url=file:///etc/passwd
# SAFE: restrict to http/https only
if parsed.scheme not in ('http', 'https'):
Web Application Firewalls are defense-in-depth, not a substitute for secure coding. Attackers bypass WAFs with encoding tricks, chunked requests, and protocol-level manipulation. Fix the vulnerability, Do not mask it.
Many organizations test for SQL injection and XSS but ignore access control, insecure design, and SSRF. Use a comprehensive testing methodology that covers all 10 categories.
Missing security headers (X-Content-Type-Options``X-Frame-Options``CSP``HSTS) provides no Defense against client-side attacks. Add them to every response.
Automated dependency scanning in CI/CD catches most known vulnerabilities. Not having this pipeline Means you are flying blind. Integrate scanning into every pull request and deployment.
OWASP Top 10 is a awareness document, not a compliance checklist. It does not cover all Vulnerabilities. Supplement with ASVS (Application Security Verification Standard), threat modeling, And regular penetration testing.
This topic covers the essential concepts and techniques related to owasp top 10 (2021) detailed, including key principles and practical applications.
Key concepts include:
- core concepts and definitions
- key principles and frameworks
- practical applications
- common techniques and methods
- evaluation and critical analysis
A thorough understanding of these concepts, combined with regular practice and review, is essential for mastery of this topic.
Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.