CybersecurityWeb Security

How to Protect APIs from Attacks: Developer Guide

TT
TopicTrick Team
How to Protect APIs from Attacks: Developer Guide

How to Protect APIs from Attacks: Developer Guide

Modern application architecture operates entirely on Application Programming Interfaces (APIs). Whether it is a React Single Page Application (SPA), an iOS mobile app, or a Server-to-Server microservice, data flows through your API. Due to their foundational importance, APIs have become the primary target for malicious actors.

An unsecured API is equivalent to leaving the master key to your database hanging on the front door. This 1,500+ word guide provides a practical framework for hardening your endpoints against modern, silicon-powered threats.


1. Hardware-Mirror: The Physics of API Latency

In a high-scale environment, security is not free. Every check you perform consumes CPU Time and Memory Bandwidth.

JWT Validation vs. Database Handshake

  • Software Logic: A JWT is "Stateless" because the server doesn't need to check a database to verify it.
  • Hardware Reality: Verifying a JWT (RS256 or ES256) requires complex mathematical operations (modular exponentiation or elliptic curve multiplication). These consume Thousands of CPU Cycles.
  • The Tradeoff: If you have 100,000 requests per second, the "Stateless" JWT validation can actually become a physical bottleneck, heating up your CPU and increasing p99 latency.

Rate Limiting: From NIC to App

LayerImplementationHardware ContextDefensive Power
Layer 3/4IPTables / NIC FiltersNetwork Card SiliconBlocks volumetric DDoS
Layer 7 (API Gateway)Nginx / Envoy / AWSReverse Proxy RAMBlocks brute-force enumeration
ApplicationMiddleware (Redis-backed)App Server CPUProtects specific logical business rules

Architecture Rule: Always "Push the Defense Down." If you can block a malicious botanist at the API Gateway layer, your application server never has to wake up the CPU or touch the RAM for that request.


How to Protect APIs from Attacks: Quick Answer

API security requires four core controls applied together: enforce object-level authorization checks so users can only access their own data, authenticate with tightly scoped tokens or httpOnly session cookies, validate and whitelist all incoming JSON fields to prevent mass assignment, and rate-limit endpoints to block brute-force and enumeration attacks. Aligning with the OWASP API Security Top 10 provides the definitive framework for this defence-in-depth approach.

The OWASP API Security Focus

While the standard OWASP Top 10 covers general web application vulnerabilities, the foundation recently released a specialized list specifically targeting APIs. This list recognizes that APIs suffer from unique structural weaknesses, particularly around object-level authorization and massive data exposure.

The Broken Object Level Authorization (BOLA) Nightmare

By far, the most critical vulnerability facing APIs today is Broken Object Level Authorization (BOLA). It occurs when an application correctly authenticates a user but fails to verify if that specific user has permission to access the requested data object.

Consider a banking application where a user requests their account details via the endpoint /api/accounts/1024.

BOLA occurs if the backend simply takes the 1024 from the URL, queries the database, and returns the data without checking if the currently logged-in user actually owns account 1024. If malicious users modify the URL to /api/accounts/1025, they might instantly access someone else's financial data.

Preventing BOLA in Code

You must always link the requested resource to the universally unique identifier (UUID) derived securely from your server-side session or JWT token, rather than blindly trusting the client-provided parameter.

javascript
// A secure approach using Express and Mongoose
app.get('/api/accounts/:accountId', requireAuth, async (req, res) => {
  try {
    const { accountId } = req.params;
    
    // Secure Identity derived safely from trusted JWT middleware
    const requestingUserId = req.user.id; 
    
    // Explicit Database query combining BOTH the resource AND the owner validation
    const account = await Account.findOne({
      _id: accountId,
      ownerId: requestingUserId // BOLA Prevention occurs here
    });
    
    if (!account) {
      return res.status(404).json({ error: "Account not found or access denied" });
    }
    
    res.json(account);
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

By mandating that the ownerId perfectly matches to the req.user.id, the attacker cannot enumerate and steal sibling accounts via parameter manipulation.

JWT vs Stateful Sessions

Authentication is the prerequisite to authorization, establishing identity. When building an API, developers frequently debate whether to use JSON Web Tokens (JWT) or traditional State-backed Sessions.

Authentication Methods

Task / FeatureStateful Sessions (Redis)Stateless JWT
No comparison data available

Unless you are strictly communicating server-to-server, modern security consensus often leans toward using stateful, tightly scoped session IDs stored in httpOnly secure cookies for web browsers to prevent catastrophic token theft via Cross-Site Scripting.

Mass Assignment: The Hidden Injection

Modern frameworks like Spring Boot, Laravel, or Express allow developers to bind HTTP request payloads directly to database objects for rapid development. This convenience introduces a severe vulnerability known as Mass Assignment.

If you update a user model by directly pushing req.body into your ORM:

javascript
// ❌ Highly Vulnerable Code
const user = await User.findById(req.user.id);
Object.assign(user, req.body); // Attacker injects {"isAdmin": true}
await user.save();

An attacker can easily append administrative flags or change their role by adding unexpected fields to the JSON payload.

Defending Against Mass Assignment

You must explicitly define the schemas detailing exactly what fields are permitted to be updated. This is often called Data Transfer Object (DTO) mapping or whitelisting.

javascript
// ✅ Secure Approach
const user = await User.findById(req.user.id);

// Only allow explicitly whitelisted properties to be updated
if (req.body.firstName) user.firstName = req.body.firstName;
if (req.body.lastName) user.lastName = req.body.lastName;
if (req.body.bio) user.bio = req.body.bio;

// The attacker's {"isAdmin": true} payload is completely ignored
await user.save();

Designing Secure API Paradigms

Securing an API requires shifting away from trusting input. By enforcing rigorous Object Level Authorization checks on every single route, explicitly validating incoming JSON structured payloads, and ensuring authentication tokens are handled securely, you eliminate the mechanisms that cause vast data leaks.

In our next web security installment, we will pivot to infrastructure, detailing exactly how to store the critical secrets, database passwords, and API keys that power these applications without inadvertently leaking them to GitHub.


4. Rate Limiting at the Hardware Layer

When an attacker launches a "Brute Force" or "Enumeration" attack, they are trying to find valid user IDs or passwords by firing millions of requests.

IRQ Polling and NIC Starvation

  • The Threat: If your application handles every single "Login Attempt," even invalid ones, your CPU spends time context-switching between your app and the kernel to handle the network interrupts.
  • The Hardware Defense: Modern Network Interface Cards (NICs) can implement Hardware-Based Rate Limiting. They look at the source IP and the frequency of packets. If an IP exceeds a threshold, the NIC drops the packets before they ever reach the CPU.
  • The Benefit: Your server stays responsive and "Cool" (literally lower thermal CPU usage) even during an active brute-force attack.

5. Rate Limiting and Throttling

Without rate limiting, an attacker can enumerate all user IDs in your database by firing thousands of requests per minute. Apply per-IP and per-user rate limits to every sensitive endpoint.

javascript
// Express rate limiting with express-rate-limit
import rateLimit from 'express-rate-limit';

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // Max 10 login attempts per window
  message: { error: "Too many login attempts. Please try again in 15 minutes." },
  standardHeaders: true, // Return RateLimit headers
  legacyHeaders: false,
});

app.post('/api/auth/login', loginLimiter, handleLogin);

For high-scale APIs, consider moving rate limiting to your API gateway (Kong, AWS API Gateway) so it applies before requests reach your application servers.

Sensitive Data Exposure Prevention

Many APIs inadvertently expose internal data by returning entire database records to the client. Always serialize only the fields the client explicitly needs.

javascript
// ❌ Exposes password hash, internal flags, and admin data
res.json(user);

// ✅ Only expose what the client needs
res.json({
  id: user.id,
  name: user.name,
  email: user.email,
  avatarUrl: user.avatarUrl
});

This is also why response schemas and explicit serialization libraries (e.g., class-transformer in TypeScript) are recommended over raw ORM objects.

API Versioning and Deprecation

Unsupported legacy API versions accumulate unpatched vulnerabilities over time. Adopt a versioning strategy from day one:

  • URI versioning: /api/v1/tasks, /api/v2/tasks
  • Header versioning: Accept: application/vnd.myapp.v2+json

Communicate deprecation timelines to API consumers with a minimum 6-month notice, and monitor which clients are still calling sunset endpoints before removing them.

Logging and Monitoring API Activity

Without logs, you cannot detect an attack in progress. At a minimum, record the following for every API request:

  • Authenticated user ID (not the token itself)
  • Endpoint path and HTTP method
  • Response status code
  • Request duration
  • Client IP address

Forward these logs to a centralized system (Datadog, AWS CloudWatch, ELK Stack) and set alerts for spikes in 401/403 responses — a common indicator of enumeration attacks.

Related Security Reading

For the authoritative API security specification, the OWASP API Security Project publishes the full Top 10 list with detailed prevention guidance for each category.

Common API Security Mistakes

1. Returning verbose error messages to clients Error responses that include stack traces, database error messages, or internal file paths leak implementation details to attackers. Return generic messages to clients ({"error": "Internal server error"}) and log full details server-side. The OWASP API Security Top 10 lists improper error handling as a leading API risk.

2. Not rate limiting authentication endpoints Login, password reset, and OTP endpoints without rate limiting are vulnerable to brute-force attacks. Implement per-IP and per-account rate limits using a sliding window counter (Redis-backed or in-memory). Return 429 Too Many Requests with a Retry-After header when limits are exceeded. Many API gateways (AWS API Gateway, Kong, Nginx) have built-in rate limiting modules.

3. Storing API keys in source code Hardcoded API keys and secrets in source code are routinely exposed in public GitHub repositories. Use environment variables (os.environ.get("API_KEY")) or a secrets manager (AWS Secrets Manager, HashiCorp Vault) to inject credentials at runtime. Tools like GitLeaks can scan repositories for accidentally committed secrets before they go public.

4. Missing CORS configuration An API with no CORS policy either rejects legitimate browser requests or, when set to Access-Control-Allow-Origin: *, allows any origin to make credentialed requests. Define an explicit allowlist of trusted origins and reject requests from others. The MDN CORS documentation is the authoritative reference.

5. Not validating and sanitising all input Every field in a request body, query string, header, and path parameter is potential attacker-controlled input. Validate type, length, and format before processing. For SQL interactions, always use parameterised queries — never string-interpolated SQL. For HTML output, escape user-supplied data to prevent XSS. See the OWASP Input Validation Cheat Sheet.

Frequently Asked Questions

What is the OWASP API Security Top 10? The OWASP API Security Top 10 is a regularly updated list of the most critical API security risks, maintained by the Open Web Application Security Project. The 2023 edition covers: Broken Object Level Authorization (BOLA), Broken Authentication, Broken Object Property Level Authorization, Unrestricted Resource Consumption, Broken Function Level Authorization, Unrestricted Access to Sensitive Business Flows, Server-Side Request Forgery, Security Misconfiguration, Improper Inventory Management, and Unsafe Consumption of APIs. It is the industry-standard reference for API security requirements.

How should I secure API keys distributed to client applications? API keys embedded in mobile apps or browser JavaScript are inherently public — a determined attacker can extract them. Mitigate this with: short-lived tokens issued per session rather than long-lived static keys; domain or bundle ID restrictions on the key's usage; rate limiting per key; monitoring for anomalous usage patterns. For high-value operations, route requests through your own backend rather than calling third-party APIs directly from the client. The Google Cloud API key best practices document many of these patterns.

What is the difference between authentication and authorisation in API security? Authentication verifies identity: "who are you?" (API key, JWT, OAuth token). Authorisation determines permissions: "what are you allowed to do?" (can this user read this resource? write to it? delete it?). Most API security vulnerabilities are authorisation failures — the API correctly identifies the user but fails to check whether that specific user is permitted to access the requested resource. Broken Object Level Authorization (BOLA/IDOR) is the most common API vulnerability in the wild. See the OWASP BOLA explanation for examples.


6. Case Study: Massive Scale API Defense (The Netflix Model)

How does a company like Netflix secure billions of API calls daily?

The Passport Architecture

  • The Problem: Passing a raw JWT through 500 microservices is expensive (CPU validation time).
  • The Solution: An Edge Gateway validates the external token once and converts it into an internal Passport.
  • The Physics: The "Passport" is a binary, highly compressed format that is verified using ultra-fast symmetric cryptography inside the internal network. This reduces the security "tax" on every microservice by 90%.

Phase 4: API Security Actions

  • Offload your JWT validation to your API Gateway or Load Balancer to save app-server CPU cycles.
  • Implement BOLA Checks on every single database query (verify ownership).
  • Enable Hardware-Accelerated Rate Limiting at your cloud perimeter (e.g., AWS Shield / Cloudflare).
  • Audit your API responses to ensure you are not leaking internal database IDs or stack traces.

Read next: Firewalls, WAFs, and Proxies Explained →