Index · How it works
7 min read- How it works
- API1: Broken Object Level Authorization (BOLA / IDOR at scale)
- API2: Broken Authentication
- API3: Broken Object Property Level Authorization (BOPLA — mass assignment + excessive data exposure)
- API4: Unrestricted Resource Consumption
- API5: Broken Function Level Authorization (BFLA)
- API6: Unrestricted Access to Sensitive Business Flows
- API7: Server-Side Request Forgery (SSRF)
- API8: Security Misconfiguration
- API9: Improper Inventory Management
- API10: Unsafe Consumption of APIs
How it works
The unifying theme across the list is that authentication is not authorization, and that authorization must be checked at three granularities: which objects you can touch (BOLA), which functions/operations you can invoke (BFLA), and which properties of an object you can read or write (BOPLA). The remaining risks cover abuse of capacity (rate/cost), abuse of legitimate business flows, and the API as both a server (SSRF, misconfiguration, unknown inventory) and a client of other APIs (unsafe consumption).
API1: Broken Object Level Authorization (BOLA / IDOR at scale)
What it is: The endpoint authenticates the caller but does not verify that the requested object belongs to (or is permitted for) that caller. This is classic IDOR, but APIs make it pervasive because nearly every endpoint takes an object id in the path, query, or body.
Example: GET /api/accounts/123/statements returns account 123's bank statements to any logged-in user, because the handler loads the statement by id and never compares the account's owner to the token's user id. An attacker increments the id and harvests everyone's data.
Defense: Enforce a per-object ownership/permission check in every handler that loads a resource by client-supplied id. Prefer scoping queries to the authenticated principal (WHERE owner_id = @currentUserId) so a non-owned id simply returns 404. Use random, unguessable ids (UUIDs) as defense-in-depth — but never as the only control. Centralize the check so it can't be forgotten per-endpoint.
API2: Broken Authentication
What it is: Weak or broken mechanisms for proving identity — guessable credentials, no brute-force/credential-stuffing protection, weak password reset, accepting unsigned or alg:none JWTs, long-lived or non-revocable tokens, or exposing API keys in URLs.
Example: A POST /api/login with no rate limiting and no lockout lets an attacker run credential stuffing; or an API trusts a JWT without validating its signature and alg, so a forged token grants admin.
Defense: Use a vetted identity provider (OAuth2/OIDC). Enforce strong password policy + MFA, rate-limit and lock out auth endpoints, and protect token endpoints with CAPTCHA/anti-automation. Validate JWT signature, alg, iss, aud, and exp; reject alg:none. Keep access tokens short-lived with rotatable refresh tokens, and support revocation. Never put credentials or tokens in query strings.
API3: Broken Object Property Level Authorization (BOPLA — mass assignment + excessive data exposure)
What it is: The 2023 merge of two older risks. Excessive data exposure is returning more object properties than the client should see (relying on the frontend to hide them). Mass assignment is binding the whole request body to your domain model, letting a client set properties they shouldn't — like isAdmin, role, balance, or verified.
Example (mass assignment): PATCH /api/users/me binds the JSON body straight onto the User entity; the attacker adds "role":"admin" and is now an admin. Example (excessive exposure): GET /api/users/{id} serializes the full entity, leaking passwordHash, internalNotes, and other users' PII.
Defense: Use explicit allow-listed input DTOs (bind only the properties the operation legitimately accepts) and output DTOs/serialization views (return only fields the caller is entitled to). Never bind directly to ORM/domain models. Authorize property access by role where needed (e.g. only support staff see internalNotes).
❌ Mass assignment — binds the entire request body to the domain model
[HttpPatch("users/me")]
public async Task<IActionResult> Update([FromBody] User incoming) {
var user = await _db.Users.FindAsync(CurrentUserId);
_mapper.Map(incoming, user); // attacker sends {"role":"admin"} → privilege escalation
await _db.SaveChangesAsync();
}
✅ Allow-listed DTO — only the fields this operation is allowed to change
public record UpdateProfileDto(string DisplayName, string Bio); // no role/isAdmin/balance
[HttpPatch("users/me")]
public async Task<IActionResult> Update([FromBody] UpdateProfileDto dto) {
var user = await _db.Users.FindAsync(CurrentUserId);
user.DisplayName = dto.DisplayName;
user.Bio = dto.Bio; // role/isAdmin are simply unreachable from the wire
await _db.SaveChangesAsync();
}
API4: Unrestricted Resource Consumption
What it is: No limits on the resources (CPU, memory, bandwidth, storage, third-party cost, money) a single client can consume. Enables denial-of-service and runaway bills.
Example: A search or export endpoint accepts ?limit=10000000, an unbounded list endpoint returns the whole table with no pagination, a file-upload accepts arbitrary sizes, or an endpoint that fans out to a paid SMS/email/LLM provider has no quota — an attacker drains the budget.
Defense: Rate-limit and throttle per client/IP/API-key. Cap and validate pagination (limit/page) with sane maximums and enforce server-side defaults. Bound request body and upload sizes, query timeouts, and result-set sizes. Add per-tenant quotas and spend limits on any endpoint that triggers paid downstream calls. Use timeouts and circuit breakers.
API5: Broken Function Level Authorization (BFLA)
What it is: The endpoint/operation itself isn't protected by the right role/permission check — a regular user can invoke admin-only or other-role functions, often just by guessing the route or changing the HTTP method.
Example: DELETE /api/users/{id} or GET /api/admin/users has no role check, so a standard user calls it directly. Or a non-admin discovers that POST /api/invoices/{id}/approve works for them too because only the admin UI hid the button.
Defense: Deny by default; require explicit authorization on every route and every method. Enforce role/permission checks centrally (policy/attribute/middleware), not by hiding UI. Separate admin endpoints clearly and protect them as a group. Test that lower-privileged tokens are rejected on privileged operations (and on each HTTP verb).
API6: Unrestricted Access to Sensitive Business Flows
What it is: A business flow (not a technical bug) is exposed without anti-automation protection, letting an attacker abuse it at scale — even though each individual request is "valid".
Example: Automated bots buy up all limited-edition stock (scalping), mass-create fake accounts, spam referral/coupon redemptions, or scrape an entire catalog. Every request is individually well-formed; the volume and intent are the abuse.
Defense: Identify sensitive flows (purchase, signup, reservation, referral) during threat modeling and add friction proportional to risk: device fingerprinting, CAPTCHA / proof-of-work, behavioral/anomaly detection, per-account and per-device limits, and blocking known bot/headless patterns. This is layered with API4 rate limiting but targets intent, not just request rate.
API7: Server-Side Request Forgery (SSRF)
What it is: The API fetches a remote resource using a URL (or host/port) influenced by the client, without validating the destination. The server then makes requests on the attacker's behalf — to internal services, cloud metadata endpoints, or arbitrary external hosts.
Example: An "import from URL" or webhook/avatar-by-URL feature does fetch(userSuppliedUrl). The attacker submits http://169.254.169.254/latest/meta-data/iam/security-credentials/ to steal cloud credentials, or http://localhost:6379 to reach internal Redis.
Defense: Allow-list permitted schemes (https only), hosts, and ports. Resolve the hostname and reject private/loopback/link-local/metadata IP ranges (and re-validate after redirects to defeat DNS rebinding). Disable or restrict redirects. Don't return raw upstream responses to the client. Run outbound fetchers in a network-segmented egress path with no access to internal services or the metadata service.
❌ SSRF-prone — fetches whatever URL the client supplies
const res = await fetch(req.body.imageUrl); // attacker → http://169.254.169.254/...
✅ Allow-listed host + scheme, block internal ranges, no redirects
const url = new URL(req.body.imageUrl);
if (url.protocol !== "https:") throw new Error("scheme");
if (!ALLOWED_HOSTS.has(url.hostname)) throw new Error("host not allowed");
const ip = await resolve(url.hostname);
if (isPrivateOrMetadata(ip)) throw new Error("blocked range");
const res = await fetch(url, { redirect: "error" });
API8: Security Misconfiguration
What it is: Insecure defaults, incomplete or ad-hoc configuration across the API stack — missing security headers, permissive CORS (Access-Control-Allow-Origin: * with credentials), verbose error messages/stack traces, unnecessary HTTP methods enabled, unpatched components, TLS not enforced, debug endpoints in production.
Example: CORS reflects any Origin and allows credentials, so a malicious site reads authenticated API responses; or a 500 returns a full stack trace and connection string.
Defense: Harden by default and keep configuration consistent and automated (IaC). Lock down CORS to known origins, enforce HTTPS/HSTS, add security headers, return generic error messages (log details server-side), disable unused methods/endpoints, patch dependencies, and run automated configuration scanning in CI.
API9: Improper Inventory Management
What it is: Not knowing all your APIs, versions, and hosts. Shadow APIs (undocumented/unknown endpoints), zombie APIs (deprecated but still live and unpatched), forgotten staging/debug hosts, and undocumented versions widen the attack surface.
Example: An old api-v1.example.com is still online with a known vuln long after v2 shipped; or a debug endpoint exposed in a non-prod host still serves production data.
Defense: Maintain an authoritative, automatically generated API inventory (every host, environment, version, and endpoint) with owners and data-sensitivity classification. Keep OpenAPI docs current and generated from code. Have a formal deprecation/retirement process; decommission old versions and non-prod hosts. Use API discovery/gateway tooling to find shadow endpoints.
API10: Unsafe Consumption of APIs
What it is: Trusting data from third-party/upstream APIs too much — applying weaker validation to responses from "trusted" integrations than to direct user input, following their redirects blindly, or not handling their failures safely.
Example: Your service ingests a partner's webhook/response and passes it straight into a SQL query or template, or follows a redirect from an upstream call to an attacker-controlled host. A compromised or malicious third party then pivots into your system.
Defense: Validate and sanitize all data from third-party APIs exactly as you would untrusted user input. Use TLS for upstream calls, validate certificates, and don't blindly follow upstream redirects. Apply timeouts, allow-list upstream endpoints, and handle/limit error and rate-limit responses. Treat the integration boundary as a trust boundary.