1. HTTPS (Transport Layer Security)
Threat Profile
MITM (Man-in-the-Middle) Attacks
Mitigation Mechanism
SSL/TLS 1.3 Encryption & HSTS
Encrypting data in transit protects against Man-in-the-Middle (MITM) attacks. Implement HSTS headers and redirect all HTTP traffic to HTTPS (preferably terminating at a reverse proxy like Nginx) to ensure all payload transmissions are fully secure.
// Force SSL and HSTS Headers using Express
const express = require('express');
const helmet = require('helmet');
const app = express();
// Redirect HTTP requests to HTTPS
app.use((req, res, next) => {
if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
next();
} else {
res.redirect(301, `https://${req.headers.host}${req.url}`);
}
});
// Configure HSTS (Strict Transport Security) via Helmet
app.use(helmet.hsts({
maxAge: 31536000, // 1 year in seconds
includeSubDomains: true,
preload: true // Opt-in to browser preload lists
}));
2. Helmet (Secure HTTP Headers)
Threat Profile
Clickjacking, XSS, MIME Sniffing
Mitigation Mechanism
HTTP Header Injection Hardening
Helmet hardens HTTP response headers to defend against clickjacking, XSS, and MIME-type sniffing. It hides stack information like `X-Powered-By: Express` and enforces Content Security Policies (CSP) to restrict script loading origins.
// Hardening HTTP headers using Helmet middleware
const express = require('express');
const helmet = require('helmet');
const app = express();
// Disable standard fingerprinting and inject security headers
app.use(helmet({
// Override Content Security Policy
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https://images.unsplash.com"],
connectSrc: ["'self'", "https://api.yourdomain.com"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
// Prevent clickjacking
frameguard: { action: 'deny' },
// Block MIME Sniffing
noSniff: true,
// Strict Referrer Policy
referrerPolicy: { policy: 'strict-origin-when-cross-origin' }
}));
3. CORS (Cross-Origin Resource Sharing)
Threat Profile
CSRF & Unauthorized Data Retrieval
Mitigation Mechanism
Strict Origin Whitelisting
Cross-Origin Resource Sharing controls which clients can query your API. A loose configuration (`*`) allows malicious sites to fetch data using a user's active session. Define a strict whitelist of trusted origins and credentials boundaries.
// Configuring strict Cross-Origin Resource Sharing
const cors = require('cors');
const express = require('express');
const app = express();
const allowedOrigins = [
'https://yourfrontend.com',
'https://admin.yourfrontend.com'
];
const corsOptions = {
origin: (origin, callback) => {
// Allow server-to-server or local tests (origin is undefined)
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Rejected by security firewall: CORS Origin mismatch.'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // Only if sharing cookies/sessions
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
4. Rate Limiting
Threat Profile
Brute Force & DDoS Attacks
Mitigation Mechanism
Request Limits by IP window
Rate limiting restricts the volume of requests from an IP window to block brute-force or DDoS attempts. Use a centralized Redis store in clustered environments to maintain accuracy across multiple server processes.
// Implementing API Rate Limiting with redis-store
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redisClient = new Redis(process.env.REDIS_URL);
const apiLimiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: {
error: 'Too many requests. Please try again after 15 minutes.'
},
standardHeaders: true, // Return rate limit info in headers
legacyHeaders: false,
});
// Apply rate limiter globally
app.use('/api/', apiLimiter);
5. Arcjet (Bot & Scraper Detection)
Threat Profile
Bots, Scrapers & automated credential stuffing
Mitigation Mechanism
Dynamic IP Fingerprinting & Behavior Audits
Standard IP limiters can be bypassed using distributed residential proxy networks. Arcjet runs application-layer bot detection and dynamic IP fingerprinting directly in your code, checking request behaviors before processing routes.
// Integrate Arcjet Bot and Scraper Detection
const express = require('express');
const arcjet = require('@arcjet/node');
const app = express();
const aj = arcjet({
key: process.env.ARCJET_KEY, // Set ARCJET_KEY env
characteristics: ["ip.src"], // Track client request origin IP
rules: [
arcjet.detectBot({
mode: "LIVE", // Reject malicious bots in real-time
// Block general automated scrapers but allow friendly search indexers
block: ["AUTOMATED", "AI"]
}),
arcjet.rateLimit({
mode: "LIVE",
algorithm: "TOKEN_BUCKET",
refillRate: 10,
capacity: 50,
interval: "1m"
})
]
});
app.use(async (req, res, next) => {
const decision = await aj.protect(req);
if (decision.isDenied()) {
return res.status(403).json({
error: "Access Denied: Automated bots or request spike detected."
});
}
next();
});
6. Request Validation (Injection Shield)
Threat Profile
SQL/NoSQL Injection, Command Injection
Mitigation Mechanism
Schema Validation via Zod
SQL, NoSQL, and Command Injection succeed when untrusted user input is passed directly to databases or shell scripts. Use schema validation libraries like Zod to validate payload shapes and sanitize types before execution.
// Request Validation Schema using Zod
const { z } = require('zod');
// Schema for user signup input
const signupSchema = z.object({
email: z.string().email({ message: "Invalid email syntax" }).trim(),
password: z.string()
.min(10, { message: "Password must be at least 10 chars long" })
.max(100)
.regex(/[A-Z]/, { message: "Require uppercase letter" })
.regex(/[0-9]/, { message: "Require number" }),
username: z.string().min(3).max(30).alphanumeric()
});
const validateRequest = (schema) => (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: "Validation failed",
details: result.error.errors.map(err => err.message)
});
}
req.validatedBody = result.data;
next();
};
app.post('/api/signup', validateRequest(signupSchema), (req, res) => {
// Safe sanitized payload is on req.validatedBody
});
{"$gt": ""} instead of a string value). Validation schema checks completely resolve this.7. Authentication (Identity Verification)
Threat Profile
Unauthorized Access & Session Hijacking
Mitigation Mechanism
Asymmetric Cryptographic Tokens (JWT RS256)
Authentication verifies requester identity. Implement JWT signature verification using asymmetric RS256 algorithms and enforce explicit signature checks to prevent public-key forgery and algorithm downgrade attacks.
// Cryptographic Identity Verification (JWT RS256)
const jwt = require('express-jwt');
const jwksRsa = require('jwks-rsa');
const verifyJWT = jwt({
// Dynamically fetch signing keys from auth server
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://auth.yourdomain.com/.well-known/jwks.json`
}),
// Validate the audience and the issuer
audience: 'https://api.yourdomain.com',
issuer: 'https://auth.yourdomain.com/',
algorithms: ['RS256'] // Block fallback to HS256/none
});
app.use('/api/protected', verifyJWT);
9. Secure Uploads (File Sandbox)
Threat Profile
Remote Code Execution (RCE), Zip Slip
Mitigation Mechanism
MIME-Type checks & Isolated Storage
File uploads are direct vectors for Remote Code Execution (RCE). Validate file extensions, size limits, and binary MIME-types. Stream uploaded files directly to isolated cloud storage instead of local application folders.
// Secure file uploads using Multer and isolated configuration
const multer = require('multer');
const path = require('path');
const storage = multer.memoryStorage(); // Stream direct to cloud buffer
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024, // Strict 5MB file limit
},
fileFilter: (req, file, callback) => {
// Validate extensions and mime-types
const allowedExtensions = ['.png', '.jpg', '.jpeg', '.webp'];
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
const fileExt = path.extname(file.originalname).toLowerCase();
const isExtOk = allowedExtensions.includes(fileExt);
const isMimeOk = allowedMimeTypes.includes(file.mimetype);
if (isExtOk && isMimeOk) {
callback(null, true);
} else {
callback(new Error('Security Error: Disallowed file extension.'));
}
}
});
10. Monitoring & Structured Logging
Threat Profile
Undetected Breach & Delayed Audits
Mitigation Mechanism
Structured JSON Logging & SIEM
Structured JSON logs make it easy to audit security events and detect incidents. Ensure log payloads are sanitized to exclude passwords, access tokens, and Personally Identifiable Information (PII) before ingestion to SIEM tools.
// Structured JSON Logging using Winston
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json() // Output JSON format for log analyzers
),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.Console()
],
});
// Custom logger sanitizer middleware
app.use((req, res, next) => {
logger.info({
method: req.method,
url: req.url,
ip: req.ip,
// NEVER log headers containing credentials, cookies or body passwords
});
next();
});
Hardening Your APIs for Production
Applying these ten layers establishes a rock-solid security foundation. Need a secure, high-performance back-end system custom built for your startup? Let's build it correctly.
Secure Your Stack