APIs have become the primary attack surface for most applications. And most developers publish complete documentation on how to attack them. Swagger/OpenAPI specs, debug endpoints, older API versions - all of this is a roadmap for attackers.
OWASP API Security Top 10
Understanding the most common API vulnerabilities:
API1: Broken Object Level Authorization (BOLA)
Most common critical API vulnerability. API doesn't verify if user has rights to access requested object.
- Example: GET /api/users/123/orders - changing 123 to 124 reveals another user's orders
- Variations: GUIDs instead of IDs aren't a solution - often predictable or leaked elsewhere
- Testing: Systematically changing all identifiers in requests
API2: Broken Authentication
Incorrect implementation of authentication mechanisms.
- JWT without signature validation - Server accepts any token
- Algorithm confusion - Changing RS256 to HS256, signing with public key
- Weak secrets - JWT signed with weak key, crackable offline
- Token leakage - Tokens in URLs, logs, error messages
API3: Broken Object Property Level Authorization
API exposes more object properties than it should, or allows modification of protected properties.
- Excessive data exposure: API returns all attributes including sensitive ones (password hash, internal IDs)
- Mass assignment: Adding extra fields to request (role: "admin", balance: 99999)
API4: Unrestricted Resource Consumption
Lack of resource consumption limits.
- No rate limiting: Enables brute force, credential stuffing, DoS
- Unbounded queries: SELECT * without LIMIT, GraphQL without depth limits
- Large file uploads: Disk or memory exhaustion
API5: Broken Function Level Authorization
Administrative functions accessible to regular users.
- Hidden admin endpoints: /api/admin/users, /api/internal/config
- HTTP method tampering: GET works, POST/DELETE also - without authorization
- Parameter manipulation: Adding admin=true to request
JWT (JSON Web Token) Attacks
JWT is a standard for authentication, but often incorrectly implemented.
JWT Structure
Header.Payload.Signature - Base64 encoded, not encrypted!
Attacks
- Algorithm None: Changing algorithm to "none", removing signature
- Algorithm Confusion: RS256 → HS256, signing with public key as secret
- Key ID Injection: kid parameter for path traversal or SQL injection
- JWK Injection: Including attacker's key in jku/jwk header
- Weak Secret Brute Force: hashcat/john for cracking HS256 secrets
- Token Expiry: Accepting expired tokens, too long exp
Protection
- Whitelist algorithms - don't accept "none" or unexpected ones
- Strong secrets for HS256 (32+ random bytes)
- Short exp times, refresh tokens
- Validate all claims (iss, aud, exp, nbf)
OAuth 2.0 Vulnerabilities
OAuth is a complex protocol with many opportunities for mistakes.
Authorization Code Flow Attacks
- Open Redirect: Manipulating redirect_uri to steal authorization code
- CSRF: Missing state parameter enables account linking attacks
- Code Reuse: Authorization code usable multiple times
- Scope Upgrade: Requesting higher scope than allowed
Implicit Flow Problems
- Token in URL: Exposed in Referer header, browser history
- Token Leakage: XSS on redirect URI page steals token
- Mix-up Attack: Attacker substitutes authorization server
Client Credentials Mistakes
- Hardcoded secrets: client_secret in frontend code, mobile apps
- Insufficient client validation: Accepting requests without client auth
GraphQL Specific Vulnerabilities
GraphQL brings new attack vectors that REST doesn't have.
Introspection
Automatic documentation of entire schema - namespace of all types, fields, mutations.
- Attack: Query __schema for complete API description
- Defense: Disable introspection in production
Query Complexity Attacks
- Deep nesting: Recursive queries (user { friends { friends { friends... }}})
- Batching: Many operations in one request
- Aliases: Same query multiple times with different aliases
- Fragments: Fragments to increase complexity
Injection
- SQL Injection: In arguments that go to database
- NoSQL Injection: Especially with MongoDB backends
GraphQL Protection
- Disable introspection in production
- Implement query depth and complexity limits
- Rate limiting per operation, not just per endpoint
- Field-level authorization
Discovering Hidden APIs
I don't just test documented endpoints.
Discovery Techniques
- Path fuzzing: /api/v1/users, /api/v2/users, /api/internal/users
- Version discovery: Older API versions without security patches
- Debug endpoints: /debug, /test, /swagger, /api-docs, /graphiql
- JavaScript analysis: API endpoints in frontend code
- Mobile app reversing: API calls in mobile application
- Wayback Machine: Old documentation versions, deprecated endpoints
Wordlists
- SecLists API wordlists
- Custom wordlists from documentation, frontend code
- Intelligent fuzzing based on discovered endpoints
Rate Limiting Bypass
Rate limiting is often implemented incorrectly.
Bypass Techniques
- IP rotation: Different IP addresses (proxy, VPN, cloud functions)
- Header manipulation: X-Forwarded-For, X-Real-IP spoofing
- Endpoint variation: /api/login, /api/Login, /api/login/, /api//login
- Parameter pollution: email=a@b.com&email=c@d.com
- Unicode normalization: admin vs ɑdmin
- Case variation: admin@domain.com vs Admin@domain.com
My Approach to API Testing
- Reconnaissance: Gathering all available documentation, swagger specs, postman collections
- Mapping: All endpoints, parameters, authentication mechanisms
- Authentication testing: JWT, OAuth, session management
- Authorization testing: BOLA, BFLA on every endpoint
- Input validation: Injection, type confusion, boundary testing
- Business logic: Workflow bypass, race conditions
- Rate limiting: Brute force, enumeration possibilities