API security means making sure every interface your systems expose is known, authenticated, authorized for each object it touches, limited in how much it can be used, and tested like any other production code. Most API weaknesses aren't exotic. They come from APIs nobody tracks, endpoints that check who you are but not what you're allowed to see, and missing limits. The OWASP API Security Top 10, updated in June 2023, is a good map of where things go wrong, and this post walks through the controls that address it.
What is API security?
An API (application programming interface) is how one piece of software asks another for data or actions. Your mobile app calls APIs. Your web front end calls APIs. Your partners, SaaS integrations and internal microservices call APIs. Much of the traffic in a modern environment is one system talking to another, with no person clicking anything.
API security covers the design, build and operation of those interfaces so they only do what they're supposed to, for callers who are supposed to use them. That's a different problem from traditional web application security. APIs expose data and business logic directly, often in a predictable structure, which makes gaps in authorization easy to find and easy to automate against.
What's in the OWASP API Security Top 10 2023?
The OWASP API Security Project released the 2023 edition of its Top 10 in June 2023, replacing the 2019 list. Here it is with a plain-English summary of each risk:
| ID | Risk | In plain terms |
|---|---|---|
| API1:2023 | Broken Object Level Authorization | A caller can access records that belong to someone else by changing an ID |
| API2:2023 | Broken Authentication | Weak or flawed handling of credentials and tokens |
| API3:2023 | Broken Object Property Level Authorization | A caller can read or change fields of an object they shouldn't |
| API4:2023 | Unrestricted Resource Consumption | No limits on requests, payload sizes or costly operations |
| API5:2023 | Broken Function Level Authorization | A regular user can call admin-only functions |
| API6:2023 | Unrestricted Access to Sensitive Business Flows | A legitimate flow can be automated and abused at scale |
| API7:2023 | Server Side Request Forgery | The API can be made to fetch a URL the attacker chooses |
| API8:2023 | Security Misconfiguration | Insecure defaults, verbose errors, missing hardening |
| API9:2023 | Improper Inventory Management | Unknown, outdated or undocumented APIs and versions |
| API10:2023 | Unsafe Consumption of APIs | Trusting data from third-party APIs without validation |
Compared with 2019, the new list merges excessive data exposure and mass assignment into a single property-level authorization category, shifts the resource item from rate limiting toward consumption in general, and adds business flows, server-side request forgery and unsafe consumption of third-party APIs. Three of the ten are authorization failures, which says a lot about where effort should go.
Start with an API inventory
You can't protect an API you don't know exists. Improper inventory management made the Top 10 for exactly that reason.
What are shadow and zombie APIs?
- Shadow APIs are live but undocumented. A team spun one up for a project, a developer exposed a debug endpoint, or a service became internet-reachable through a configuration change.
- Zombie APIs are old versions that were supposed to be retired but still answer requests. They often lack the fixes and controls added to newer versions.
Both tend to be less protected than the APIs you're watching, and both show up in inventories built from real data rather than memory.
How to build the inventory
Pull from several sources:
- API gateway and load balancer configurations
- OpenAPI or other API specifications stored in code repositories
- Ingress rules, DNS records and TLS certificates for API hostnames
- Traffic and access logs showing which endpoints actually receive requests
- Service catalogs and architecture documentation, treated as a starting point rather than truth
For each API, record an owner, whether it's internal, partner-facing or public, what data it handles, how callers authenticate, and which versions are live. Then set a deprecation process with announced sunset dates, and actually turn old versions off when the date arrives.
Authentication: know who's calling
Every API call should carry an identity you can verify. Match the method to the caller:
- User-facing APIs: OAuth 2.0 and OpenID Connect, with short-lived access tokens.
- Machine-to-machine: the OAuth 2.0 client credentials flow, or mutual TLS between services.
- API keys: fine for identifying a client and applying quotas, but weak as the only proof of identity. Treat them as secrets, scope them narrowly and rotate them.
If you use JSON Web Tokens, validate them properly on every request. Check the signature with an allowlisted algorithm, reject unsigned tokens, and verify the issuer, audience and expiry. Many authentication bugs come from skipping one of those checks.
How to prevent broken object level authorization (BOLA)
BOLA sits at the top of the OWASP list, and it's simple to explain. Suppose an authenticated customer requests GET /api/invoices/1043 and gets their invoice. They change the number to 1044 and get someone else's. The API checked that the caller was logged in, but not that they were allowed to see that specific invoice.
This happens because authorization depends on business rules the framework can't know. Each endpoint that accepts an object ID has to make the check itself, and it only takes one endpoint that forgets.
To prevent it:
- Check ownership in every handler that reads, updates or deletes an object by ID, not just at the route level.
- Centralize the logic in a shared authorization function or policy layer, so developers call one well-tested check instead of writing their own.
- Take identity from the token, not the request. The user or tenant ID should come from the validated token, never from a parameter the caller controls.
- Use random identifiers such as UUIDs to make enumeration harder. Treat this as a speed bump, not a control.
- Test with two users. Automated tests should confirm that user A cannot read, change or delete user B's objects.
The same thinking applies to properties and functions. Return only the fields a caller needs, reject fields they shouldn't be able to set (such as role or isAdmin), and check permissions on admin functions rather than relying on the front end to hide them.
Rate limiting and resource consumption
Every API call costs something: CPU, memory, database queries, bandwidth, or money when your API calls a paid third-party service. Without limits, a single client, whether buggy or hostile, can degrade the service for everyone or run up a large bill.
Set limits at several levels:
- Requests per client over a time window, returning HTTP 429 (Too Many Requests) when exceeded
- Payload and upload sizes, plus the number of items per batch request
- Page sizes, with a maximum enforced on the server regardless of what the client asks for
- Query complexity, particularly for GraphQL, where depth and cost limits matter
- Timeouts on requests and downstream calls
- Spending limits on third-party services your API triggers
Sensitive business flows need separate attention. Account creation, checkout, booking and reward redemption can all be abused through automation even when each individual request is valid. Apply limits per account and per flow, not only per IP address, and watch for patterns that no real user would produce.
What does an API gateway do, and what doesn't it do?
An API gateway sits in front of your services and gives you one place to enforce common controls:
- Terminating TLS and enforcing modern protocol versions
- Validating tokens and API keys
- Rate limiting and quotas
- Request size limits and, often, schema validation
- Consistent logging of every call
What a gateway can't do is object-level authorization. It doesn't know whether invoice 1044 belongs to the caller. That check needs business context and belongs in the service. Also make sure services can't be reached around the gateway, or its controls only apply to callers who politely use it.
Validate requests and responses against a schema
An API specification, typically in OpenAPI format, is a contract that describes each endpoint, parameter and field. Treat it as a security control as well as documentation:
- Validate incoming requests against the schema: types, formats, lengths, ranges and required fields.
- Reject unknown properties. This stops callers from sneaking in fields like
rolethat the endpoint wasn't meant to accept. - Define response schemas and return only what they list, so internal fields don't leak.
- Keep specs in version control next to the code. They double as your inventory and as input for testing.
Schema validation won't catch authorization flaws, but it narrows what an endpoint will accept and makes misuse easier to spot.
How to test API security
Testing should run continuously, not once a year:
- In CI: unit and integration tests for authorization, including multi-user tests for BOLA and role tests for admin functions.
- Automated scanning: dynamic scanners that import your OpenAPI specs, such as the open-source OWASP ZAP, can exercise every documented endpoint.
- Manual testing: business logic flaws and authorization gaps usually need a person who understands what the API is supposed to do. Include APIs in the scope of your penetration tests.
- Production monitoring: alert on spikes in 401 and 403 responses, sequential ID access patterns and calls to deprecated versions.
Don't forget the APIs you consume. Treat responses from third-party services as untrusted input, validate them, set timeouts, and restrict any feature that fetches a user-supplied URL to an allowlist of destinations.
Frequently asked questions
What is the most common API vulnerability?
OWASP ranks broken object level authorization first in the 2023 list. It's common because each endpoint must check ownership itself, and the flaw is easy to find by simply changing an ID.
Are API keys enough to secure an API?
Not on their own. An API key identifies a client, but it's a static secret that's easy to leak and rarely tied to a specific user. Use OAuth 2.0 or mutual TLS for authentication, and keep API keys for identification and quotas.
Does an API gateway replace secure coding?
No. A gateway handles authentication, rate limits and logging consistently, but object-level and business-logic authorization still have to live in the service code.
Next steps
- Build an API inventory from gateways, specs, DNS and traffic logs, and assign an owner to each API.
- Retire zombie versions on a published schedule.
- Audit every endpoint that takes an object ID for an ownership check, and add two-user tests.
- Set rate, size and page limits, plus per-account limits on sensitive business flows.
- Put public APIs behind a gateway and validate requests against OpenAPI schemas.
- Add API-aware scanning to CI and include APIs in your next penetration test.