API Security Best Practices: Authentication, Rate Limiting, and Input Validation | SoniNow Blog

Limited TimeLearn More

api securityauthenticationrate limitingvalidationbackend

API Security Best Practices: Authentication, Rate Limiting, and Input Validation

Published

2026-06-23

Read Time

4 mins

API Security Best Practices: Authentication, Rate Limiting, and Input Validation

APIs are the backbone of modern web applications, connecting frontend clients, third-party integrations, microservices, and internal tools. Each exposed endpoint is a potential entry point for attackers, and API breaches now account for the majority of data exposure incidents.

API Key Lifecycle Management

Static API keys are the simplest authentication mechanism but require careful lifecycle management. Keys should be generated using cryptographically secure randomness, stored as hashed values in the database, and never logged or included in source code.

import crypto from 'crypto';

function createApiKey(): { prefix: string; hashedKey: string; rawKey: string } {
  const rawKey = `sn_${crypto.randomBytes(32).toString('hex')}`;
  const prefix = rawKey.substring(0, 8); // Store prefix for identification
  const hashedKey = crypto
    .createHash('sha256')
    .update(rawKey)
    .digest('hex');

  return { prefix, hashedKey, rawKey };
}

Return the full key only once during creation. Allow multiple active keys per user or service so expiration can happen without disruption. Implement key rotation policies — revoke old keys after 90 days and force regeneration.

OAuth 2.0 Token Validation for APIs

When accepting OAuth 2.0 bearer tokens, the API must validate the token on every request. For self-issued JWTs, verify the signature using the issuer's public key, check the exp claim, and validate the aud (audience) against the expected resource server.

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({ jwksUri: 'https://auth.example.com/.well-known/jwks.json' });

async function verifyToken(token: string): Promise<JwtPayload> {
  const getKey = (header: jwt.JwtHeader, cb: jwt.SigningKeyCallback) => {
    client.getSigningKey(header.kid!, (err, key) => {
      cb(err, key?.getPublicKey());
    });
  };

  return new Promise((resolve, reject) => {
    jwt.verify(token, getKey, {
      algorithms: ['RS256'],
      audience: 'api.soninow.com',
      issuer: 'https://auth.soninow.com',
    }, (err, decoded) => {
      if (err) reject(err);
      else resolve(decoded as JwtPayload);
    });
  });
}

For third-party token validation (Google, GitHub, Microsoft), use the provider's token introspection endpoint rather than reimplementing JWKS fetching. Provider-issued tokens have different claim structures and key rotation schedules.

Rate Limiting Strategies

Rate limiting protects APIs from abuse, brute force attacks, and unintentional traffic spikes. Implement multiple tiers of rate limiting — global, per-endpoint, and per-user — with decreasing thresholds.

import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

const apiLimiter = rateLimit({
  store: new RedisStore({ client: redisClient }),
  windowMs: 60 * 1000, // 1 minute
  max: 100,
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: 'Too many requests', retryAfter: 60 },
});

app.use('/api/', apiLimiter);

// Stricter limit for auth endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true,
});

app.use('/api/auth/login', authLimiter);

Return 429 Too Many Requests with a Retry-After header. Use Redis or another distributed store for rate limit state so limits apply consistently across all application instances. Consider token bucket or sliding window algorithms over fixed window counters to avoid burst traffic at window boundaries.

Input Validation at the API Gateway

Validate every field of every request before it reaches your application logic. Define schemas for all endpoints and reject malformed payloads at the earliest possible point.

import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email().max(255),
  name: z.string().min(1).max(100).trim(),
  role: z.enum(['user', 'admin', 'viewer']).default('user'),
  metadata: z.record(z.string(), z.unknown()).optional(),
});

app.post('/api/users', (req, res) => {
  const result = CreateUserSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(422).json({
      error: 'Validation failed',
      details: result.error.flatten().fieldErrors,
    });
  }
  // Process validated data
});

Reject unexpected fields, enforce length limits on all string inputs, and validate numeric ranges. SQL injection and NoSQL injection are prevented at this layer by rejecting input types that don't match the schema.

CORS Configuration

Cross-Origin Resource Sharing is frequently misconfigured. Restrict Access-Control-Allow-Origin to specific origins rather than using * for credentialed requests. Validate the Origin header against a whitelist rather than echoing it back.

const corsOptions = {
  origin: [
    'https://app.soninow.com',
    'https://admin.soninow.com',
  ],
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-Id'],
  exposedHeaders: ['X-RateLimit-Remaining'],
  credentials: true,
  maxAge: 86400,
};

Request Signing for Server-to-Server Communication

For internal microservice communication, HMAC-based request signing provides integrity and authenticity. The sender signs the request body plus headers using a shared secret, and the receiver verifies the signature before processing.

function signRequest(payload: string, secret: string): string {
  return crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
}

Include a timestamp in the signature and reject requests with more than five minutes of clock skew to prevent replay attacks.

APIs are the most attacked surface area in modern applications. Our <a href="/services/web-development">API development services</a> embed security into every endpoint from design through deployment. Contact SoniNow to audit your API security posture.