API Security Fundamentals: The Attack Surface You Exposed on Purpose
APIs are doors you deliberately opened to the world. The core practices that keep them from becoming the easiest way in.
Modern software is APIs all the way down — services talking to services, apps talking to backends, partners talking to your platform. Each API is, by design, a door you opened to the outside on purpose, and collectively they have become one of the largest and most attacked surfaces in software. API security is not an exotic specialty; it is a core discipline every team shipping an endpoint needs to understand, because attackers certainly do.
The good news is that the majority of API breaches trace back to a handful of well-understood, preventable weaknesses.
Authentication and authorization are different
The most common and damaging API flaws live in access control, and the crucial distinction is between authentication (who are you?) and authorization (what are you allowed to do?). An API can correctly verify a user's identity and still fail catastrophically by not checking whether that user is allowed to access the specific object they requested — letting anyone read anyone else's data by changing an ID in the URL. This class of broken object-level authorization is consistently among the top real-world API vulnerabilities.
The rule: authenticate every request, and then authorize every access against the specific resource and action, on the server, every time. Never trust the client to enforce what it may see.
Validate everything from the outside
An API cannot trust anything a caller sends. Every input needs validation — types, ranges, formats, sizes — because unvalidated input is the road to injection attacks, resource exhaustion and logic abuse. Reject what does not match expectations rather than trying to sanitize the unexpected. Return errors that reveal as little as possible; verbose error messages hand attackers a map. And be strict about what your API accepts, not permissive.
Mass-assignment flaws, where a caller sets fields they should not (like their own role or account balance), are a reminder to bind explicitly to allowed fields rather than trusting the whole payload.
Rate limits, quotas and monitoring
APIs need protection against volume as well as content. Rate limiting and quotas prevent brute-force attacks, credential stuffing, scraping and denial of service, and they cap the damage a compromised key can do. Without them, a single leaked credential can be used to exfiltrate an entire dataset one call at a time. Monitoring for anomalous patterns — a client suddenly requesting far more than usual, a spike in errors, access from unexpected places — turns silent abuse into a caught event.
Manage the keys
Finally, the credentials that guard APIs need real lifecycle management: secrets stored securely rather than hard-coded, scoped narrowly to what each caller needs, rotated regularly, and revocable instantly when leaked. A key with god-mode permissions, committed to a repository and never rotated, is a breach waiting to be found by a scanner.
None of these fundamentals are exotic. Enforce object-level authorization on every request, validate all input, rate-limit and monitor, and manage keys with care, and you close the doors through which the great majority of API attacks actually walk.
Know what you expose
You cannot protect an endpoint you have forgotten exists. Many API incidents involve old versions left running after a migration, internal endpoints accidentally reachable from the internet, or test environments connected to real data. Keep an inventory generated from the code or the gateway rather than from memory: every host, every version, every route, who owns it and what data it touches. Machine-readable descriptions such as an OpenAPI specification make this easier and double as documentation for reviews. Retire old versions on a schedule, and treat an unowned endpoint as a finding to fix, not a curiosity.
Object-level checks in practice
Broken object-level authorization is easy to describe and easy to miss. The pattern is always the same: a request names a resource by identifier, and the server returns it without confirming that the caller is entitled to that particular record. The cure is a consistent rule applied in one place — for example, every query for customer data is scoped by the tenant and user taken from the verified token, never from the request body or URL. Random, unguessable identifiers make enumeration harder, but they are not a substitute for the check; an identifier that leaks in a log or a shared link must still not grant access.
Function-level authorization
A related failure occurs when ordinary users can call administrative functions simply because the endpoint exists and nothing stops them. Hiding a button in the interface does not protect the operation behind it. Every privileged action — changing roles, issuing refunds, exporting data, deleting accounts — needs an explicit server-side permission check. Deny by default, so that a new endpoint without a declared permission is inaccessible rather than open. Group permissions into clear roles, and review them when features change, because permission sprawl creeps in quietly.
Return only what is needed
APIs often leak data not through a missing check but through generous responses. An endpoint that returns the whole user object, including internal flags, email addresses or password reset fields, relies on the client to show only part of it — and attackers read the raw response. Define response schemas deliberately and serialize only the fields each consumer needs. The same discipline applies to error responses: stack traces, database messages and internal hostnames help attackers map your system. Log the detail on the server and return a short, generic message with a reference the support team can look up.
Tokens and sessions
Most APIs authenticate with tokens, and their handling deserves care. Keep access tokens short-lived and use refresh mechanisms rather than tokens that never expire. Use scopes so that a token issued for reading a calendar cannot also delete files. If you use signed tokens such as JSON Web Tokens, validate the signature with a fixed, expected algorithm, check the issuer, audience and expiry, and never accept unsigned tokens. Avoid placing tokens in URLs, where they end up in logs and browser history. And plan revocation: when a user logs out or a device is lost, the associated tokens must stop working promptly.
Transport, CORS and browsers
All API traffic should travel over TLS, including traffic between internal services, where attackers who gain a foothold can otherwise listen. For APIs called from browsers, configure cross-origin resource sharing narrowly: list the specific origins allowed, and do not reflect any origin back with credentials enabled, which effectively disables the browser's protection. Remember that CORS protects users' browsers, not your server; a script or command-line tool ignores it entirely, so it never replaces authentication and authorization on the server.
Server-side request forgery
Features that fetch a URL on the user's behalf — importing an image, previewing a link, calling a webhook — can be abused to make your server reach places the attacker cannot, such as internal admin panels or cloud metadata services that hand out credentials. Validate destinations against an allow-list where possible, block private and link-local address ranges after resolving the hostname, disable redirects or re-check each hop, and run such fetchers with minimal network access. This class of flaw has been behind several large cloud breaches, and it is far cheaper to design out than to clean up.
Expensive operations and resource limits
Rate limits count requests, but not all requests cost the same. A single search with a huge page size, a deeply nested query, a batch endpoint processing thousands of items or a file upload with no size cap can exhaust memory or database capacity. Set maximum page sizes, bound query depth and complexity, limit upload sizes and request timeouts, and push long-running work into queues with their own limits. For flows tied to money or reputation — sign-ups, password resets, messages, purchases — add business-level limits that make automated abuse unprofitable.
Versioning and change control
Every change to an API is a chance to reintroduce a fixed vulnerability or expose a field unintentionally. Review changes to authentication, authorization and response schemas with particular care, and use automated contract tests to catch accidental additions. When you publish a new version, give the old one a deprecation date and enforce it. Running several versions side by side indefinitely multiplies the attack surface and means security fixes must be applied, and tested, in many places.
Testing API security
Build security checks into the normal development cycle. Unit and integration tests should include negative cases: a user requesting another user's record, an ordinary account calling an admin function, malformed and oversized inputs, expired and tampered tokens. Automated scanners can catch common misconfigurations, while periodic manual testing by someone who thinks like an attacker finds logic flaws that tools miss. Consider a vulnerability disclosure policy so that outside researchers who find problems know how to report them to you rather than publishing them.
Logging for investigations
When something goes wrong, you will need to answer which keys were used, by whom, against which endpoints and objects, and how much data left. Log authentication results, authorization failures, administrative actions and unusual volumes, with request identifiers that tie events together across services. Avoid recording secrets or full payloads containing personal data. Alert on spikes in authorization failures, which often signal someone probing identifiers, and on any single client suddenly retrieving far more records than usual.
A checklist for every new endpoint
Before shipping an endpoint, ask: who may call it, and how is that verified? Does it check access to each specific object? Which inputs does it accept, and are they validated against strict schemas? What exactly does it return? What limits apply to request rate, size and cost? Are its actions logged? Is it documented and owned? Answering these seven questions takes minutes during design and prevents the flaws that most often turn up in real-world API breaches.
Run AI on your own terms
AXTO gives you world-class AI apps and self-hosted editions — on your own keys, your own data.