APIs Are Under Siege
APIs now account for over 83% of all web traffic (Cloudflare 2025). Gartner predicted that API attacks would become the most frequent attack vector by 2025—and they were right. From the T-Mobile breach (API-based data scraping of 37 million records) to the Optus incident (unauthenticated API exposing 10 million customer records), the pattern is clear: if you build APIs, securing them is no longer optional.
Why API Security Is Different
Traditional web application security focused on protecting rendered pages—XSS, CSRF, and session management. APIs change the game fundamentally:
- Machine-to-machine: APIs are consumed by code, not humans. There's no browser sandbox, no CAPTCHA, and no visual cues when something is wrong.
- Direct data access: APIs expose raw business logic and data structures. An API
endpoint returning
GET /api/users/12345is far more dangerous than a web page displaying a user profile. - Massive surface area: A typical microservices architecture has hundreds of internal and external API endpoints, many undocumented or forgotten ("shadow APIs").
- Stateless by design: Each request must carry its own authentication context, making token management and validation critical at every endpoint.
The OWASP API Security Top 10 (2023)
The OWASP API Security Project defines the ten most critical API security risks. Understanding these is the foundation of any API security program:
The #1 API vulnerability. Occurs when an API doesn't verify that
the requesting user owns the object they're accessing. Example: changing
/api/orders/123 to /api/orders/124 to view another user's order.
Fix: enforce authorization checks on every object access, not just at the endpoint level.
Weak token generation, missing token validation, credential stuffing vulnerabilities. Fix: use industry-standard OAuth 2.0 / OpenID Connect flows, enforce token expiry, implement refresh token rotation.
APIs returning too many properties (mass assignment / excessive
data exposure). Example: GET /api/user/me returns
{"name":"Alice","role":"admin","ssn":"123-45-6789"}. Fix: explicit response
schemas that whitelist returned fields per role.
No rate limiting, allowing attackers to exhaust resources via API abuse. Fix: implement rate limiting, pagination, request size limits, and cost-based throttling for expensive operations.
Regular users accessing admin functions. Example:
POST /api/admin/users/delete accessible to non-admin tokens. Fix: enforce RBAC
at every function endpoint, not just at the UI level.
Authentication: Getting the Foundation Right
API authentication determines who is making the request. Getting this wrong is catastrophic. Here's the hierarchy of approaches, from worst to best:
- ❌ API Keys alone: Easy to implement but easy to leak. API keys are identifiers, not secrets—they should be used for tracking and rate limiting, not as sole authentication.
- ⚠️ Basic Auth over HTTPS: Sends credentials with every request. Acceptable only for server-to-server with mutual TLS.
- ✅ OAuth 2.0 + JWT: The industry standard. Use the
Authorization Codeflow with PKCE for user-facing apps,Client Credentialsfor service-to-service. Always validate JWT signature, issuer, audience, and expiry server-side. - ✅ Mutual TLS (mTLS): Both client and server present certificates. The gold standard for service-to-service in Zero Trust architectures and defense environments.
// JWT validation middleware (Node.js / Express example)
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.status(401).json({ error: 'Missing token' });
jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
algorithms: ['RS256'], // Enforce algorithm
issuer: 'https://auth.example.com', // Validate issuer
audience: 'api.example.com', // Validate audience
}, (err, decoded) => {
if (err) return res.status(403).json({ error: 'Invalid token' });
req.user = decoded;
next();
});
}
Authorization: Object-Level Is Non-Negotiable
Authentication tells you who someone is. Authorization tells you what they can do. The most common API breach pattern is: authenticated user accesses objects they don't own (BOLA).
The fix is deceptively simple in theory but requires discipline in practice: every data access must verify ownership, not just that the user is authenticated.
// ❌ Vulnerable: Only checks authentication
app.get('/api/documents/:id', authenticate, async (req, res) => {
const doc = await Document.findById(req.params.id);
res.json(doc); // Any authenticated user can access ANY document
});
// ✅ Secure: Checks authentication AND ownership
app.get('/api/documents/:id', authenticate, async (req, res) => {
const doc = await Document.findOne({
_id: req.params.id,
owner: req.user.id // Enforce ownership
});
if (!doc) return res.status(404).json({ error: 'Not found' });
res.json(doc);
});
Rate Limiting and Throttling
Without rate limiting, your API is an all-you-can-eat buffet for attackers. Effective rate limiting operates at multiple layers:
- Global rate limit: Maximum requests per IP per time window (e.g., 1000/minute). Protects against volumetric abuse.
- Per-user rate limit: Maximum requests per authenticated user. Prevents credential stuffing and data scraping.
- Per-endpoint rate limit: Sensitive endpoints (login, password reset, data export) get stricter limits.
- Cost-based throttling: Expensive operations (report generation, search queries, AI inference) consume more "tokens" from the user's rate limit budget.
Always return standard rate limit headers so legitimate clients can implement backoff:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1709312400
{
"error": "Rate limit exceeded",
"retry_after": 30
}
Input Validation: Trust Nothing
Every API input—headers, path parameters, query strings, request bodies—is an attack vector. The principle is simple: validate, sanitize, and reject everything that doesn't match your explicit schema.
- Schema validation: Use OpenAPI/Swagger schemas to define exactly what each endpoint accepts. Reject requests that don't match the schema before they reach your business logic.
- Type coercion: Ensure
idparameters are integers, not strings that could become NoSQL injection payloads like{"$gt": ""}. - Size limits: Enforce maximum request body size, maximum array length, and maximum string length to prevent resource exhaustion.
- Content-Type enforcement: Only accept the content types your API actually supports.
Reject
application/xmlif you only expectapplication/jsonto prevent XXE attacks.
// Schema validation with Zod (TypeScript)
import { z } from 'zod';
const CreateOrderSchema = z.object({
productId: z.string().uuid(), // Must be valid UUID
quantity: z.number().int().min(1).max(100), // Integer, 1-100
shippingAddress: z.object({
street: z.string().max(200),
city: z.string().max(100),
zipCode: z.string().regex(/^\d{5}(-\d{4})?$/),
country: z.string().length(2), // ISO 3166-1 alpha-2
}),
notes: z.string().max(500).optional(),
});
// In your route handler:
const result = CreateOrderSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.issues });
}
API Gateway: Centralized Security Enforcement
An API gateway is the single entry point for all API traffic, providing centralized enforcement of security policies. Without one, every microservice must independently implement authentication, rate limiting, logging, and CORS—a recipe for inconsistency and gaps.
Key gateway capabilities:
- Authentication offloading: Validate JWTs at the gateway, passing only verified identity claims to backend services.
- Rate limiting: Enforce global and per-client limits before requests reach your services.
- Request/response transformation: Strip sensitive headers, redact fields from responses based on caller permissions.
- WAF integration: Block known attack patterns (SQL injection, XSS payloads) at the edge.
- API versioning: Route traffic to different service versions without client changes.
- Logging and observability: Capture request/response metadata for audit trails and anomaly detection.
Popular choices include Kong, AWS API Gateway, Azure API Management, and Envoy (for service mesh deployments in Kubernetes environments).
Securing GraphQL APIs
GraphQL introduces unique security challenges that REST doesn't have:
- Query depth attacks: Deeply nested queries can cause exponential backend lookups. Enforce a maximum query depth (typically 5-10 levels).
- Query complexity analysis: Assign a cost to each field and reject queries exceeding a complexity budget before execution.
- Introspection in production: Disable schema introspection in production. Attackers use it to map your entire data model.
- Batching abuse: A single GraphQL request can contain multiple operations. Limit batch size and apply rate limits per operation, not per HTTP request.
- Field-level authorization: Unlike REST (where endpoints map to resources), GraphQL resolvers must enforce authorization at every field resolution.
# ❌ Dangerous: No depth limit
query {
user(id: 1) {
friends {
friends {
friends {
friends {
# ...exponential backend load
}
}
}
}
}
}
# ✅ Solution: Enforce depth limit
# graphql-depth-limit middleware
depthLimit(5) # Reject queries deeper than 5 levels
API Security in CI/CD
Security testing should happen at every stage of the API lifecycle, not just in production. Integrate these checks into your DevSecOps pipeline:
- Design phase: Threat model every new API endpoint. Each endpoint is a trust boundary.
- Build phase: Static analysis (SAST) to detect hardcoded secrets, SQL injection patterns, and missing authorization checks.
- Test phase: Dynamic API security testing (DAST) using tools like OWASP ZAP, Burp Suite, or Nuclei to fuzz endpoints with malicious payloads.
- Deploy phase: Validate that the deployed API matches the OpenAPI spec. Detect undocumented endpoints ("shadow APIs") that bypass security controls.
- Runtime phase: Continuous API monitoring for anomalous patterns—unusual request volumes, new client fingerprints, response code spikes.
API Security Checklist
A prioritized checklist for teams securing their API surface:
- Enforce HTTPS everywhere. No exceptions. Enable HSTS.
- Implement OAuth 2.0 / mTLS for all authentication. Retire API-key-only auth.
- Add object-level authorization checks at every data access point.
- Deploy an API gateway for centralized auth, rate limiting, and logging.
- Validate all inputs against explicit schemas. Reject anything unexpected.
- Rate limit at global, per-user, and per-endpoint levels.
- Version your APIs and deprecate old versions with clear timelines.
- Log everything. Request/response metadata, authentication events, authorization failures. Forward to your SIEM.
- Inventory all APIs. Discover shadow APIs and deprecated endpoints that still accept traffic.
- Test continuously. Integrate DAST into CI/CD and run penetration tests quarterly.
Alterra Solutions' Perspective
At Alterra, we build APIs that handle classified data, process real-time defense intelligence, and power mission-critical systems where a security failure isn't just a data breach—it's a national security event. Our API security approach is built on mTLS by default, schema-first design with automated contract testing, and continuous runtime monitoring that feeds directly into our incident response playbooks.
If your APIs handle sensitive data and you need a security baseline that goes beyond the defaults, let's talk.