Skip to content
Back to Journal
Cybersecurity

Modern API Security: Rate Limiting, CORS, and Zero Trust

Gagan Shukla
12 min read 6 views
Modern API Security: Rate Limiting, CORS, and Zero Trust

Exposing an API to the public internet is akin to opening a storefront on the busiest, most dangerous street in the world. Within minutes of deployment, automated botnets, vulnerability scanners, and malicious actors will begin probing your endpoints for weaknesses.

To build APIs that power enterprise applications, simply validating a JWT is no longer enough. You must implement defensive, zero-trust architectures at every layer of the network stack.

Here is how elite engineering teams secure highly scalable APIs in 2026.

The API Gateway: Your First Line of Defense

Never expose your application servers directly to the internet. All incoming traffic must first pass through an API Gateway or a Web Application Firewall (WAF) such as Cloudflare, AWS API Gateway, or Kong.

The API Gateway is responsible for terminating SSL/TLS connections, filtering out malformed requests, and blocking known malicious IP ranges before they ever reach your Node.js or Express runtime. This offloads immense CPU overhead from your application logic.

Advanced Rate Limiting Strategies

A simple rate limiter restricts an IP address to $N$ requests per minute. While necessary, this naive approach is easily bypassed by attackers rotating through distributed proxy networks.

Modern rate limiting must be multi-dimensional:

  1. IP-Based Throttling: The baseline. Limit requests per IP address to prevent brute-force attacks.
  2. User-Based Throttling: Limit requests per authenticated user_id. This prevents a malicious user from routing their attacks through 500 different IP proxies.
  3. Endpoint Specific Quotas: An endpoint like GET /posts can handle 100 req/sec. An endpoint like POST /auth/login or POST /reset-password should be hard-capped at 3 req/minute to prevent brute-forcing.

Implementing Redis-backed Rate Limiting

In distributed systems, an in-memory rate limiter on a single Node instance will fail. You must use a centralized fast-storage system like Redis to track quotas globally.

javascript
1import rateLimit from 'express-rate-limit';
2import RedisStore from 'rate-limit-redis';
3import { redisClient } from './redis.js';
4
5export const loginRateLimiter = rateLimit({
6  store: new RedisStore({
7    sendCommand: (...args) => redisClient.sendCommand(args),
8  }),
9  windowMs: 15 * 60 * 1000, // 15 minutes
10  max: 5, // Limit each IP to 5 login requests per window
11  standardHeaders: true,
12  legacyHeaders: false,
13  message: { error: 'Too many login attempts. Please try again later.' }
14});

CORS: The Misunderstood Shield

Cross-Origin Resource Sharing (CORS) is a browser security mechanism, not a backend firewall. It prevents a malicious website (e.g., evil.com) from making authenticated AJAX requests to your API (api.yourdomain.com) on behalf of a victim user.

Many developers, frustrated by CORS errors during development, set Access-Control-Allow-Origin: *. This is catastrophic for authenticated APIs.

Strict CORS configuration is mandatory:

javascript
1import cors from 'cors';
2
3const allowedOrigins = [
4  'https://gaganshukla.in',
5  'https://admin.gaganshukla.in'
6];
7
8app.use(cors({
9  origin: function (origin, callback) {
10    // Allow requests with no origin (like mobile apps or curl)
11    if (!origin) return callback(null, true);
12    if (allowedOrigins.indexOf(origin) === -1) {
13      return callback(new Error('Blocked by CORS Policy'), false);
14    }
15    return callback(null, true);
16  },
17  credentials: true, // Crucial for accepting HttpOnly cookies
18  methods: ['GET', 'POST', 'PUT', 'DELETE']
19}));

Preventing Data Exposure through Pagination

One of the most devastating forms of API abuse is data scraping. If you expose an endpoint like GET /users, an attacker can write a script to download your entire database.

Pagination is not just for UI/UX; it is a critical security measure.

  1. Hard Limits: Always enforce a maximum limit (e.g., limit=100). If a client requests limit=10000, truncate it to 100 on the backend.
  2. Cursor Pagination: For massive datasets, offset/limit pagination degrades the database because it requires scanning all preceding rows. Cursor-based pagination (?after=cursor_id) relies on indexes and operates in $O(1)$ time complexity.

Zero Trust and Input Sanitization

The core tenet of Zero Trust is: Never trust the client.

Every single byte of data entering your API must be validated, sanitized, and typed before it touches your database. If you expect a string, ensure it isn't an object containing a MongoDB $ne operator (NoSQL injection). If you expect an integer, ensure it isn't a massive buffer designed to crash your Node runtime.

Libraries like Zod or Joi are non-negotiable for modern REST APIs.

typescript
1import { z } from 'zod';
2
3const userSchema = z.object({
4  email: z.string().email().max(255),
5  password: z.string().min(12).max(100),
6  age: z.number().int().min(18).max(120).optional()
7});
8
9export const validatePayload = (req, res, next) => {
10  try {
11    req.body = userSchema.parse(req.body);
12    next();
13  } catch (error) {
14    res.status(400).json({ error: error.errors });
15  }
16};

Conclusion

API Security is a layered defense model. By utilizing edge-based firewalls, implementing sophisticated rate limiting, rigidly enforcing CORS policies, and strictly validating payloads, you insulate your core application logic from the chaos of the public internet. Secure by default is the only way to build for the future.


Frequently Asked Questions

Can CORS prevent server-to-server attacks? No. CORS is enforced by the web browser. If an attacker uses cURL, Postman, or a Python script to hit your API, CORS is completely bypassed. This is why rate limiting and authentication are vital.

What is the difference between a WAF and an API Gateway? An API Gateway focuses on routing, rate limiting, and analytics (handling traffic). A Web Application Firewall (WAF) inspects the actual payload of the traffic, blocking SQL injections, cross-site scripting attempts, and known malicious IPs before they reach the gateway.

Why should I limit the size of incoming JSON payloads? If you don't limit payload sizes (e.g., express.json({ limit: '10kb' })), an attacker can send a 500MB JSON object. Parsing this massive object in memory will block Node's single thread and crash your server, resulting in an immediate Denial of Service (DoS).

Share
Gagan Shukla

Written by Gagan Shukla

Full Stack Mobile App Developer specializing in Flutter, React Native, and cross-platform application development.

Read Next