APIs are the backbone of modern applications — and the fastest-growing attack surface in cybersecurity. The 2023 T-Mobile breach exposed 37 million customer records through an unsecured API. The Optus breach in 2022 (11 million Australians exposed) was caused by an API without authentication. The OWASP API Security Top 10 exists because API attacks are now responsible for more data breaches than traditional web attacks.
OWASP API Security Top 10 (2023)
# The 10 most critical API security risks:
API1:2023 - Broken Object Level Authorization (BOLA/IDOR)
API2:2023 - Broken Authentication
API3:2023 - Broken Object Property Level Authorization
API4:2023 - Unrestricted Resource Consumption
API5:2023 - Broken Function Level Authorization (BFLA)
API6:2023 - Unrestricted Access to Sensitive Business Flows
API7:2023 - Server Side Request Forgery
API8:2023 - Security Misconfiguration
API9:2023 - Improper Inventory Management
API10:2023 - Unsafe Consumption of APIs
API1: Broken Object Level Authorization (BOLA)
# BOLA = IDOR in API context
# Attacker accesses another user's data by changing an ID in the request
# VULNERABLE endpoint:
GET /api/v1/orders/1234 # Returns order 1234 for current user
# Attacker changes to:
GET /api/v1/orders/1235 # Returns ANOTHER user's order!
# Fix: Always verify the requesting user owns the object:
# Python/Flask example:
@app.route('/api/v1/orders/')
@require_auth
def get_order(order_id):
order = Order.query.get(order_id)
if not order:
return jsonify({'error': 'Not found'}), 404
# CRITICAL CHECK: Does this user own this order?
if order.user_id != g.current_user.id:
return jsonify({'error': 'Forbidden'}), 403
return jsonify(order.to_dict())
API2: Broken Authentication
# Common authentication failures in APIs:
# 1. API keys in URL parameters (logged in server logs, browser history):
# BAD: GET /api/data?api_key=secret123
# GOOD: Authorization: Bearer secret123 (in header)
# 2. Weak JWT validation:
# BAD: Accept JWT with algorithm "none":
import jwt
# Attacker creates: {"alg":"none","typ":"JWT"}
# Most vulnerable libraries accept this!
# FIX: Explicitly specify allowed algorithms:
token_data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# Never: algorithms=["HS256", "none"]
# 3. No token expiration:
# JWT should expire in short windows (15 min for access tokens)
token = jwt.encode({
"user_id": user.id,
"exp": datetime.utcnow() + timedelta(minutes=15) # 15 min expiry
}, SECRET_KEY)
# 4. Missing rate limiting on auth endpoints:
# Implement with Flask-Limiter:
from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address)
@app.route('/api/auth/login', methods=['POST'])
@limiter.limit("5 per minute") # Max 5 login attempts per minute
def login(): ...
API4: Unrestricted Resource Consumption
# APIs without rate limits are vulnerable to:
# - Brute force attacks
# - Scraping (stealing your data)
# - DoS via expensive operations
# Example: API that sends SMS codes
# Without limits: attacker can trigger 10,000 SMS to victim's number
# Fix: Implement rate limiting per user, IP, and API key:
# Nginx rate limiting:
# limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# server {
# location /api/ {
# limit_req zone=api burst=20 nodelay;
# }
# }
# Also limit: response size, query complexity (GraphQL),
# pagination depth, and concurrent connections
API Security Headers
# Essential security headers for APIs:
# In Express.js:
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: false, # APIs usually don't need CSP
}));
# Manual headers:
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Access-Control-Allow-Origin', 'https://yourdomain.com'); # Specific origin, not *!
next();
});
# CORS misconfiguration (very common):
# BAD: Access-Control-Allow-Origin: * with credentials
# This allows any website to make authenticated API calls on behalf of a user
API Security Testing
# Test your own APIs:
# 1. OWASP ZAP active scan:
docker run -v $(pwd):/zap/wrk owasp/zap2docker-stable zap-api-scan.py \
-t https://api.yourdomain.com/openapi.json -f openapi
# 2. Nuclei API templates:
nuclei -u https://api.yourdomain.com -t apis/
# 3. Manual testing with Burp Suite:
# Import your API's OpenAPI/Swagger spec
# Burp Pro generates test cases automatically
# 4. Test BOLA manually:
# Create two accounts, get a resource ID from account A,
# try accessing it from account B's session
Wrap Up
API security is non-negotiable in 2025. BOLA and broken authentication account for the majority of API breaches. Implement object-level authorization checks on every endpoint, use short-lived JWT tokens, enforce rate limiting, and scan your APIs with ZAP or nuclei before they go to production.