Imported from frank-luongt/faos-skills-marketplace (
skills/cowork/owasp-api-top10/SKILL.md). Install upstream withnpx skills add frank-luongt/faos-skills-marketplace --skill owasp-api-top10. Copyright stays with the author.
name: owasp-api-top10 description: OWASP API Security Top 10 (2023) risk identification, prevention patterns, and testing guidance for securing REST/GraphQL APIs tags: [api, owasp]
OWASP API Security Top 10 (2023)
Overview
The OWASP API Security Top 10 is a standardized awareness document for API-specific security risks. Unlike the general OWASP Top 10 (which focuses on web applications), this list addresses the unique attack surface of APIs -- broken object-level authorization, mass assignment, rate limiting gaps, and more. APIs are the backbone of modern architectures (microservices, mobile backends, SaaS integrations), making them a primary target for attackers.
This skill covers all 10 risks from the 2023 edition, provides concrete attack scenarios, and maps each risk to prevention patterns applicable to Python/FastAPI codebases.
When to Use This Skill
- Designing or reviewing API endpoints (REST, GraphQL, gRPC)
- Performing threat modeling on microservice architectures
- Conducting API security code reviews or penetration tests
- Building authorization middleware or rate-limiting layers
- Creating an API security checklist for CI/CD pipelines
- Evaluating third-party API integrations for security posture
How It Works
Step 1: Inventory API Endpoints
Catalog every API endpoint in the system. Use OpenAPI/Swagger specs as the source of truth. For each endpoint, document the HTTP method, path, required authentication, authorization scope, and data sensitivity classification.
# Extract all routes from a FastAPI app
python -c "
from app.main import app
for route in app.routes:
if hasattr(route, 'methods'):
for method in route.methods:
print(f'{method:7s} {route.path}')
"
Step 2: Classify Data Sensitivity
Map each endpoint's request/response payloads to sensitivity tiers:
| Tier | Examples | Controls Required |
|---|---|---|
| Critical | Passwords, tokens, PII, financial data | Encryption at rest + transit, audit logging, field-level access control |
| High | Email, phone, user preferences | Access control, audit logging |
| Medium | Public profile data, non-sensitive metadata | Authentication required |
| Low | Health checks, public catalog data | Rate limiting only |
Step 3: Map Authentication and Authorization Requirements
For every endpoint, define:
- AuthN: Which authentication mechanism (JWT, API key, OAuth2, mTLS)?
- AuthZ: Which authorization model (RBAC, ABAC, ReBAC)? What resource ownership checks are needed?
- Scope: What OAuth2 scopes or permission claims are required?
Step 4: Test with Security Scanning Tools
Run automated scans using OWASP ZAP, Burp Suite, or Nuclei against staging environments:
# OWASP ZAP API scan with OpenAPI spec
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
-t https://staging.example.com/openapi.json \
-f openapi \
-r zap-report.html
# Nuclei scan for API-specific vulnerabilities
nuclei -u https://staging.example.com/api \
-t http/vulnerabilities/ \
-t http/misconfiguration/ \
-severity critical,high
Step 5: Implement Controls for Each Risk
Apply the prevention patterns below for each of the 10 risks. Prioritize by likelihood and impact in your specific architecture.
The 10 API Security Risks
API1:2023 - Broken Object Level Authorization (BOLA)
Description: APIs expose endpoints that handle object identifiers, creating a wide attack surface. An attacker substitutes the ID of a resource they own with the ID of a resource belonging to another user. Lack of authorization checks lets the attacker access or modify the target resource.
Attack Scenario: GET /api/v1/invoices/12345 -- attacker changes 12345 to 12346 and receives another tenant's invoice because the endpoint only checks authentication, not ownership.
Prevention:
- Enforce object-level authorization on every endpoint that accesses a resource by ID
- Use the authenticated user's session/token to derive tenant context -- never trust client-supplied tenant IDs
- Write authorization as a reusable dependency, not inline per-route logic
API2:2023 - Broken Authentication
Description: Authentication mechanisms are implemented incorrectly, allowing attackers to compromise tokens, exploit implementation flaws, or assume other users' identities.
Attack Scenario: Credential stuffing against /api/v1/auth/login with no rate limiting or account lockout. Weak JWT validation accepts tokens signed with alg: none.
Prevention:
- Use established libraries for JWT validation (python-jose, PyJWT) with explicit algorithm allow-lists
- Implement rate limiting on authentication endpoints (stricter than general endpoints)
- Enforce MFA for sensitive operations
- Rotate and expire tokens with short TTLs; use refresh token rotation
API3:2023 - Broken Object Property Level Authorization
Description: APIs expose object properties that the user should not be able to read (excessive data exposure) or modify (mass assignment). This combines the former API3 (Excessive Data Exposure) and API6 (Mass Assignment) from the 2019 edition.
Attack Scenario: GET /api/v1/users/me returns {"name": "Alice", "role": "user", "internal_credit_score": 780}. Or: PUT /api/v1/users/me with {"role": "admin"} succeeds because the endpoint blindly accepts all fields.
Prevention:
- Define explicit response schemas that exclude sensitive internal fields
- Use allow-lists for writable fields on update endpoints (never pass raw request body to ORM)
- Implement field-level authorization for sensitive properties
API4:2023 - Unrestricted Resource Consumption
Description: APIs do not limit the size or number of resources that can be requested, leading to denial of service, brute-force attacks, or cost escalation (e.g., SMS/email flooding).
Attack Scenario: GET /api/v1/products?page_size=999999 returns the entire catalog in one response. Or: /api/v1/otp/send is called 10,000 times with no rate limit, running up SMS costs.
Prevention:
- Enforce max page sizes and default pagination on all list endpoints
- Implement rate limiting per user/IP/API key with sliding windows
- Set request body size limits and query complexity limits (GraphQL depth/breadth)
- Budget external API calls (SMS, email) with per-user quotas
API5:2023 - Broken Function Level Authorization
Description: Attackers send API calls to endpoints they should not have access to. These may be administrative functions or endpoints of a different user role. The API relies on the client to enforce access control.
Attack Scenario: Regular user calls DELETE /api/v1/admin/users/42 or POST /api/v1/admin/config -- admin endpoints exist but authorization middleware is missing or misconfigured.
Prevention:
- Deny by default -- require explicit authorization decorators on every route
- Separate admin and user API routers with distinct middleware stacks
- Implement role-based route guards as framework middleware, not per-handler logic
API6:2023 - Unrestricted Access to Sensitive Business Flows
Description: APIs expose business flows (purchasing, posting reviews, booking) that can be automated and abused at scale without proper anti-automation controls.
Attack Scenario: Bot purchases all limited-edition items via /api/v1/checkout faster than human users. Or: automated review posting to manipulate ratings.
Prevention:
- Identify business-critical flows and add anti-automation (CAPTCHA, device fingerprinting)
- Implement velocity checks (e.g., max 3 purchases per user per hour)
- Monitor for anomalous patterns (burst traffic from single user/IP)
API7:2023 - Server Side Request Forgery (SSRF)
Description: An API fetches a remote resource based on a user-supplied URL without validating the target. Attackers use SSRF to access internal services, cloud metadata endpoints, or port-scan internal networks.
Attack Scenario: POST /api/v1/webhooks {"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"} -- the server fetches AWS credentials from the metadata service.
Prevention:
- Validate and sanitize all user-supplied URLs against an allow-list of domains/protocols
- Block requests to private IP ranges (10.x, 172.16-31.x, 192.168.x, 169.254.x, localhost)
- Use a dedicated egress proxy for outbound requests with URL filtering
- Disable HTTP redirects or validate the redirect target
API8:2023 - Security Misconfiguration
Description: APIs and their supporting infrastructure (API gateways, cloud services, frameworks) are misconfigured, leaving default credentials, unnecessary HTTP methods, verbose error messages, or missing security headers.
Attack Scenario: CORS set to Access-Control-Allow-Origin: * with credentials. Debug mode enabled in production exposes stack traces. TLS 1.0/1.1 still enabled.
Prevention:
- Harden CORS (specific origins, no wildcard with credentials)
- Disable debug/verbose error modes in production
- Enforce TLS 1.2+ with strong cipher suites
- Remove default credentials and unnecessary HTTP methods (TRACE, OPTIONS)
- Automate configuration audits in CI/CD
API9:2023 - Improper Inventory Management
Description: Organizations lose track of API versions, deprecated endpoints, and shadow APIs. Old or unmanaged API versions remain exposed without security patches.
Attack Scenario: Production runs API v3 with proper auth, but v1 (/api/v1/users) is still accessible with weaker authentication and returns more data than v3.
Prevention:
- Maintain a machine-readable API inventory (OpenAPI specs in version control)
- Decommission old API versions with hard deadlines and client migration plans
- Use API gateways to enforce version routing and block deprecated versions
- Scan for undocumented endpoints (shadow API discovery)
API10:2023 - Unsafe Consumption of APIs
Description: Developers trust data from third-party APIs without validation. If a third-party API is compromised or returns malicious data, the consuming application becomes vulnerable.
Attack Scenario: Application consumes a partner API that returns user display names. A compromised partner injects <script> tags in names, causing stored XSS when rendered.
Prevention:
- Validate and sanitize all data received from third-party APIs
- Apply the same input validation rules as for user input
- Use allow-lists for expected data formats and reject unexpected structures
- Implement circuit breakers and timeouts for external API calls
Examples
Example 1: Detecting and Preventing BOLA (API1) in FastAPI
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import get_current_user, CurrentUser
from app.database import get_db
router = APIRouter(prefix="/api/v1/invoices", tags=["invoices"])
# BAD: No ownership check -- vulnerable to BOLA
@router.get("/{invoice_id}")
async def get_invoice_insecure(invoice_id: int, db: AsyncSession = Depends(get_db)):
invoice = await db.get(Invoice, invoice_id)
if not invoice:
raise HTTPException(status_code=404)
return invoice # Any authenticated user can access any invoice
# GOOD: Ownership check prevents BOLA
@router.get("/{invoice_id}")
async def get_invoice_secure(
invoice_id: int,
current_user: CurrentUser = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
invoice = await db.get(Invoice, invoice_id)
if not invoice:
raise HTTPException(status_code=404)
# Enforce object-level authorization
if invoice.tenant_id != current_user.tenant_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to access this resource",
)
return InvoiceResponse.model_validate(invoice) # Explicit response schema
Example 2: Rate Limiting for API4 (Python/FastAPI with SlowAPI)
from fastapi import FastAPI, Request
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
limiter = Limiter(
key_func=get_remote_address,
default_limits=["100/minute"],
storage_uri="redis://localhost:6379/0",
)
app = FastAPI()
app.state.limiter = limiter
app.add_middleware(SlowAPIMiddleware)
# Stricter limit on authentication endpoints (API2 + API4)
@app.post("/api/v1/auth/login")
@limiter.limit("5/minute")
async def login(request: Request):
...
# Stricter limit on sensitive business flows (API6)
@app.post("/api/v1/checkout")
@limiter.limit("3/minute")
async def checkout(request: Request):
...
# Standard limit on data endpoints with pagination enforcement (API4)
@app.get("/api/v1/products")
@limiter.limit("60/minute")
async def list_products(request: Request, page: int = 1, page_size: int = 20):
MAX_PAGE_SIZE = 100
page_size = min(page_size, MAX_PAGE_SIZE) # Enforce ceiling
...
Example 3: API Inventory Management with OpenAPI Spec
# openapi-inventory.yaml -- central API inventory for API9 prevention
openapi: "3.1.0"
info:
title: "FAOS Platform API Inventory"
version: "2024-01-15"
description: "Canonical inventory of all active API endpoints"
paths:
/api/v3/users:
get:
summary: "List users (current version)"
x-api-status: active
x-data-classification: high
x-auth-required: true
x-auth-scopes: ["users:read"]
/api/v1/users:
get:
summary: "List users (DEPRECATED)"
x-api-status: deprecated
x-deprecation-date: "2023-06-01"
x-sunset-date: "2024-03-01"
x-migration-guide: "https://docs.example.com/migrate-v1-v3"
deprecated: true
# CI check: fail build if any path has x-api-status: deprecated past x-sunset-date
# CI script to enforce API inventory hygiene
#!/usr/bin/env bash
set -euo pipefail
TODAY=$(date +%Y-%m-%d)
DEPRECATED=$(yq '.paths | to_entries[] |
select(.value[].x-api-status == "deprecated") |
select(.value[]."x-sunset-date" < env(TODAY)) |
.key' openapi-inventory.yaml)
if [ -n "$DEPRECATED" ]; then
echo "ERROR: The following deprecated APIs are past their sunset date:"
echo "$DEPRECATED"
exit 1
fi
Best Practices
Do This
- Enforce object-level authorization on every endpoint that accepts a resource identifier
- Use explicit Pydantic response models to control exactly which fields are exposed
- Implement rate limiting at the API gateway and application layers (defense in depth)
- Maintain a versioned OpenAPI spec as the single source of truth for your API surface
- Log all authorization failures with sufficient context for incident response
- Validate all URLs before making server-side requests (SSRF prevention)
- Apply pagination with enforced maximum page sizes on all list endpoints
- Treat third-party API responses with the same suspicion as user input
Don't Do This
- Do not rely solely on authentication to protect resources -- AuthN is not AuthZ
- Do not expose internal object IDs without authorization gates
- Do not pass raw request bodies to ORM update methods (mass assignment)
- Do not set CORS to
*withallow_credentials=True - Do not leave deprecated API versions running without a sunset plan
- Do not trust client-side rate limiting or client-supplied tenant/user IDs
- Do not make outbound HTTP requests to user-supplied URLs without validation
- Do not return stack traces or internal error details in production responses
Security Checklist
Authorization (API1, API3, API5)
- Every endpoint accessing a resource by ID includes ownership/tenant verification
- Response schemas explicitly list returned fields (no ORM model passthrough)
- Update endpoints use allow-listed writable fields (no mass assignment)
- Admin endpoints are behind role-based authorization middleware
- Default-deny policy: routes without explicit auth decorators are blocked
Authentication (API2)
- JWT validation uses explicit algorithm allow-list (no
alg: none) - Authentication endpoints have stricter rate limits than data endpoints
- Token TTLs are short (15-30 minutes for access tokens)
- Refresh token rotation is implemented (one-time use refresh tokens)
- Password reset and OTP endpoints have brute-force protection
Resource Consumption (API4, API6)
- All list endpoints enforce maximum page sizes
- Rate limiting is configured per user/IP with sliding windows
- Request body size limits are enforced at the gateway and application level
- Sensitive business flows have anti-automation controls (CAPTCHA, velocity checks)
- External API call budgets are enforced (SMS, email quotas)
Infrastructure (API7, API8)
- SSRF protections block private IP ranges and metadata endpoints
- CORS is configured with specific allowed origins (no wildcard + credentials)
- TLS 1.2+ enforced; TLS 1.0/1.1 disabled
- Debug mode and verbose error responses are disabled in production
- Security headers present (X-Content-Type-Options, X-Frame-Options, CSP)
Inventory and Integration (API9, API10)
- API inventory is maintained in version control (OpenAPI specs)
- Deprecated API versions have documented sunset dates and migration plans
- Shadow API discovery scans run periodically
- Third-party API responses are validated and sanitized before use
- Circuit breakers and timeouts are configured for all external API calls
Related Skills
- @api-security-patterns -- Detailed implementation patterns for API security controls
- @owasp-top10 -- General web application security risks (broader than API-specific)
- @cwe-sans-top25 -- Software weakness classification with CWE IDs for root-cause analysis