Secure APIs in Node.js
/ 2 min read
Table of Contents
API Security Fundamentals
Exposing an API without security measures leads to vulnerabilities like unauthorized access, data leaks, and injection attacks. This guide covers:
- Authentication & Authorization
- Rate Limiting
- Input Validation
- Secure Headers & OWASP Best Practices
Authentication: JWT for Secure User Sessions
Use JSON Web Tokens (JWT) to authenticate users.
Issue JWT on Login
import jwt from "jsonwebtoken";
const secretKey = "your_secret_key";
function generateToken(userId: string) { return jwt.sign({ userId }, secretKey, { expiresIn: "1h" });}
Verify JWT in Middleware
import { Request, Response, NextFunction } from "express";import jwt from "jsonwebtoken";
const secretKey = "your_secret_key";
export function authMiddleware( req: Request, res: Response, next: NextFunction,) { const token = req.headers.authorization?.split(" ")[1]; if (!token) return res.status(401).json({ error: "Unauthorized" });
try { const decoded = jwt.verify(token, secretKey); req.user = decoded; next(); } catch { res.status(403).json({ error: "Invalid token" }); }}
Rate Limiting to Prevent Abuse
Prevent API abuse by limiting request rates per user.
import rateLimit from "express-rate-limit";
const limiter = rateLimit({ windowMs: 15 _ 60 _ 1000, // 15 minutes max: 100, // Limit 100 requests per window});
app.use(limiter);
For distributed rate limiting, use Redis.
import { createClient } from "redis";
const redisClient = createClient();await redisClient.connect();
const key = `rate_limit:${req.ip}`;const requests = await redisClient.incr(key);if (requests > 100) return res.status(429).json({ error: "Too many requests" });
await redisClient.expire(key, 900);
Input Validation to Prevent Injection Attacks
Validate user input using Zod.
import { z } from "zod";
const userSchema = z.object({ username: z.string().min(3).max(20), email: z.string().email(), password: z.string().min(8),});
function validateUserInput(input: unknown) { return userSchema.parse(input);}
If input is invalid, Zod
throws an error, preventing SQL Injection and XSS.
Secure HTTP Headers with Helmet
Use Helmet.js to add security headers.
import helmet from "helmet";
app.use(helmet());
Headers Added:
X-Frame-Options: DENY
(Prevents Clickjacking)X-XSS-Protection: 1; mode=block
(Mitigates XSS)Strict-Transport-Security
(Forces HTTPS)
OWASP Security Best Practices
- Use HTTPS – Prevents MITM attacks.
- Salt & Hash Passwords – Use bcrypt.
- Avoid Exposing Stack Traces – Set
NODE_ENV=production
. - Limit CORS – Restrict API access to trusted domains.
import cors from "cors";
app.use(cors({ origin: "https://yourdomain.com" }));
Conclusion
- Use JWT for authentication.
- Rate limit to prevent API abuse.
- Validate inputs to prevent injections.
- Apply secure HTTP headers.
- Follow OWASP best practices.
Securing an API is an ongoing process—regularly audit and monitor security logs.