API Security
itOffensive security and application security
API Security
An application programming interface, or API, defines how one software component asks another component to read data or perform an action. API security protects that exchange from unauthorized use, unsafe input, excessive consumption, and unintended disclosure.
The useful mental model is a guarded contract at every trust boundary. The contract describes allowed operations, inputs, outputs, and errors. The guards establish identity, authorize the exact action, validate the exchange, limit consumption, and record evidence.
client identity and request
↓
transport → gateway controls → service controls → business logic
↓ ↓
authorization data and actions
↓ ↓
response validation ← response
An API gateway can apply shared controls. It cannot know every object, field, workflow state, or business rule. The service that owns a resource must still enforce those decisions.
Why APIs need their own security view
APIs expose application logic and data in a form that software can call repeatedly. A legitimate client can become an attack tool by changing an object identifier or adding a property. It can also switch an HTTP method, replay a workflow step, or automate a sensitive business flow.
The browser or mobile interface is not a security boundary. Attackers call the API directly. They can ignore hidden buttons, client-side validation, navigation order, and assumptions built into the official client.
Internal APIs need the same security reasoning. A network location does not prove which service or user initiated a call. Authenticate the calling service where the architecture requires it. Preserve the end-user identity when one service acts for a user. Authorize both identities against the requested operation and resource.
API security applies before and during runtime:
- Pre-runtime controls define the contract, schemas, ownership, permissions, data classifications, tests, and retirement plan.
- Runtime controls encrypt traffic, authenticate callers, authorize actions, validate messages, enforce limits, and produce telemetry.
The two sets depend on each other. Runtime enforcement is unreliable when the organization does not know which APIs exist or what their contracts permit.
Start with the API contract
An API contract describes the operations a client can call. It also describes request and response shapes, field types, required values, errors, and authentication expectations.
Treat the contract as security input, not only as documentation. A precise schema lets you reject unknown fields and oversized values. It also rejects unsupported content types and malformed responses before business logic processes them.
For each API, track:
- owner and business purpose;
- environment and exposure;
- consumers and calling identities;
- version and lifecycle state;
- operations, schemas, and authentication method;
- data classifications and external data flows;
- dependencies and upstream providers;
- runtime service instances and policy status.
Reconcile declared contracts with observed traffic. This finds shadow APIs that bypass review, beta hosts with weaker controls, and deprecated versions that still process production data.
Separate authentication from authorization
Authentication verifies an identity claim. Authorization decides whether that identity may perform this action on this resource now.
A valid token answers only part of the security question. The service must validate the token for its expected issuer, audience, lifetime, and intended use. It must then evaluate authorization using trusted server-side state.
OAuth two point zero is an authorization framework, not an authentication protocol. An API key usually identifies or meters a client. It should not stand in for user authentication on sensitive resources.
For every non-public operation, ask:
Which service is calling?
Which user or workload caused the call?
Which operation is requested?
Which object is targeted?
Which properties may be read or changed?
Which workflow state permits the action?
This produces several distinct authorization layers.
Object-level authorization
Broken object-level authorization occurs when a caller can change an identifier and reach another object without permission. A random identifier makes guessing harder, but it does not grant access. Check the caller's permission on the resolved object for every read, update, and delete.
Property-level authorization
An identity can be allowed to access an object without being allowed to read or change every property. Build response shapes from an allowlist. Bind request bodies to explicit input types. Reject unknown or forbidden fields.
Function-level authorization
A caller can be allowed to read a resource without being allowed to delete it or invoke an administrative operation. Deny by default. Grant operations explicitly. Apply the same policy regardless of URL naming, HTTP method, protocol, or client interface.
Workflow authorization
An individually valid operation may still be invalid at the current stage. The server must enforce transitions such as create, approve, pay, and finalize. Do not rely on the client to call them in order.
Validate messages in both directions
Treat every request value as untrusted. This includes paths, query strings, headers, cookies, body fields, filenames, URLs, and values received from another API.
Validate syntax and meaning:
- accept only documented methods and content types;
- enforce types, formats, ranges, lengths, array counts, and nesting depth;
- reject unknown properties where the contract does not allow them;
- validate identifiers before resolving objects;
- constrain query complexity, pagination, batch size, and upload size;
- use safe parsers and parameterized interfaces;
- validate responses before returning or consuming them.
Response validation matters because serialization can expose internal fields. It also protects your system when an upstream provider returns malformed or attacker-controlled data.
Do not treat a trusted provider as a trusted payload. Encrypt the connection, authenticate the peer, limit redirects, set timeouts, cap response size, and validate returned data before it reaches an interpreter.
Tokens are evidence, not permission by themselves
Bearer tokens are usable by whoever possesses them. Protect them in transit and storage. Keep them out of URLs and logs. Use short, appropriate lifetimes and narrow scopes and audiences.
For JSON Web Tokens, configure allowed algorithms rather than accepting the token header's choice. Verify integrity. Validate issuer, audience, expiration, and any application-required claims. Use distinct validation rules for different token kinds so one token cannot be substituted for another.
OAuth security depends on the flow as well as the token. Current IETF guidance favors authorization code flows and protects public clients with proof key for code exchange. It also restricts token privilege and recommends sender-constrained access tokens where appropriate. Refresh tokens need replay protection through sender constraint or rotation.
Availability includes work and cost
Counting requests is not enough. One request can trigger a large upload, an expensive query, many batched operations, a third-party charge, or a long-running job.
Limit each resource dimension that matters:
| Dimension | Representative limit |
|---|---|
| Request rate | Calls per identity and time window |
| Concurrency | In-flight work per caller and service |
| Payload | Bytes, fields, array items, and nesting |
| Query | Page size, complexity, execution time |
| Workflow | Attempts per operation and business object |
| Dependency | Timeouts, retries, response size, and spend |
| Storage | Upload size, retained objects, and expansion |
A rate limit protects service capacity over a short interval. A quota controls total use over a longer period. Use both when the threat model requires them. Add timeouts, cancellation, circuit breakers, backpressure, and spending alerts.
Business abuse needs business-aware controls. A ticket purchase, reservation, referral credit, or password reset may be technically valid and still harmful when automated at scale. Identify sensitive flows during design. Then combine identity, per-object limits, state checks, bot defenses, and monitoring around the actual harm.
Control server-side requests
An API that fetches a user-supplied URL can become a proxy into internal services. This is server-side request forgery.
Prefer fixed destinations. When variable destinations are required, allowlist schemes, origins, ports, and media types. Parse URLs with a maintained parser. Block internal and link-local targets. Disable redirects or validate every redirect target. Isolate the fetcher and never return raw upstream responses.
Network egress policy reduces impact. Input validation alone cannot cover every parser, redirect, and name-resolution edge case.
The gateway is one enforcement point
An API gateway is useful for consistent transport policy, coarse authentication, routing, rate limits, and telemetry. A web application firewall can detect known patterns. Neither control understands all application state.
Keep decisions near the data they protect:
| Decision | Natural enforcement point |
|---|---|
| TLS and accepted protocol | Edge, gateway, and service connection |
| Coarse token validation | Gateway and receiving service |
| Object and property authorization | Resource-owning service |
| Workflow transition | Business service |
| Schema validation | Every trust boundary |
| Resource limit | Gateway, service, database, and dependency |
| Data disclosure | Service constructing the response |
Layer controls so bypassing one route does not bypass the policy. A direct service path, asynchronous consumer, administrative endpoint, or old API version must receive equivalent protection.
Operate an API security lifecycle
Security work follows the API from design through retirement.
- Inventory. Identify every API, owner, environment, version, consumer, identity, and data flow.
- Specify. Define operations, schemas, permissions, error behavior, limits, and lifecycle policy.
- Threat model. Examine objects, properties, functions, workflows, dependencies, and resource consumption.
- Build. Use standard authentication, explicit data types, safe parsers, and secure defaults.
- Test. Vary identities, objects, fields, methods, workflow order, payload size, concurrency, and upstream behavior.
- Deploy. Enforce equivalent controls across edge, gateway, service, and asynchronous paths.
- Monitor. Correlate logs, metrics, and traces with API, operation, caller, decision, and target.
- Respond. Revoke credentials, block a caller, reduce limits, isolate a dependency, and preserve evidence.
- Retire. Migrate consumers, remove routes and credentials, verify traffic stops, and delete obsolete data paths.
Test authorization as a matrix. For each role or workload, attempt every relevant operation against owned, shared, foreign, and nonexistent objects. Test allowed and forbidden properties separately. Repeat the matrix across every API version and alternate route.
Log authentication results, authorization denials, validation failures, sensitive operations, limit events, configuration changes, and dependency failures. Avoid recording credentials, full tokens, or unnecessary sensitive payloads.
What API security cannot promise
The OWASP API Security Top 10 is an awareness taxonomy. It helps teams recognize common API-specific risks. It is not a complete control catalog, a system-specific threat model, or proof that an API is secure.
TLS does not repair broken authorization. A valid token does not make every requested object permissible. An API gateway does not replace service checks. A schema does not understand business abuse. A penetration test does not keep inventory, configuration, and dependencies current.
API security is the repeated work of keeping the contract, enforcement, observed behavior, and business intent aligned.
A practical adoption path
- Build an inventory from contracts, deployments, gateways, domain records, and observed traffic.
- Choose one high-impact API and draw every caller, service, data store, and dependency.
- Write an authorization matrix for operations, objects, properties, and workflow states.
- Make request and response schemas explicit and reject anything outside the contract.
- Add limits for rate, concurrency, payload, query cost, workflow attempts, and provider spend.
- Test the matrix through every route, including old versions and service-to-service calls.
- Correlate logs, metrics, and traces with identities, decisions, objects, and policy versions.
- Define containment and retirement procedures before the next incident or migration.
