Skip to content

24 Security Best Practices

DodaTech 4 min read

title: Security Best Practices for Node.js REST APIs weight: 34 date: 2026-06-28 lastmod: 2026-06-28 description: Learn security best practices for Node.js REST APIs including helmet headers, input sanitization, SQL injection prevention, rate limiting, CORS, and dependency auditing. tags: [api-development, nodejs]


Security best practices for Node.js REST APIs include HTTP security headers with Helmet, input validation and sanitization, SQL/NoSQL injection prevention, proper authentication, rate limiting, CORS configuration, and regular dependency auditing.

```mermaid
flowchart TD
  A[Security Layers] --> B[Helmet Headers]
  A --> C[Input Validation]
  A --> D[Auth + Rate Limiting]
  A --> E[Dependency Audit]
  B --> F[CSP, X-Frame-Options]
  C --> G[Sanitize, Escape]
  D --> H[JWT, Rate Limit]
  E --> I[npm audit, Snyk]
  style A fill:#e1f5fe
  style B fill:#c8e6c9
  style C fill:#c8e6c9
  style D fill:#fff9c4
  style E fill:#fff9c4

Security is layered: Helmet sets HTTP headers (CSP, X-Frame-Options, HSTS). Input validation rejects malformed data. Parameterized queries prevent injection. Authentication verifies identity. Authorization checks permissions. Rate limiting prevents abuse. Regular audits catch vulnerable dependencies.

Think of API security like a castle defense. Helmet is the outer wall (headers). Input validation is the gate check (who enters). Auth is the inner guards (identification). Authorization is the room keys (access level). Rate limiting is the crowd control at the gate.

Example: Helmet Security Headers

const helmet = require('helmet');

// Default: sets 15 security headers
app.use(helmet());

// Custom configuration
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"]
    }
  },
  frameguard: { action: 'deny' },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

Example: Input Sanitization and Injection Prevention

const express = require('express');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

// Prevent NoSQL injection
app.use(mongoSanitize({
  replaceWith: '_',
  onSanitize: ({ req, key }) => {
    console.warn(`Sanitized ${key} in request`);
  }
}));

// Prevent XSS attacks
app.use(xss());

// SQL injection prevention with Prisma (parameterized queries)
app.get('/api/users', async (req, res) => {
  // Prisma parameterizes queries automatically
  const users = await prisma.user.findMany({
    where: {
      name: { contains: req.query.search } // Safe: parameterized
    }
  });
  res.json({ data: users });
});

Example: Comprehensive Security Middleware

const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');

// Security middleware stack
app.use(helmet());

// CORS
app.use(cors({
  origin: process.env.CORS_ORIGIN || 'https://myapp.com',
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
}));

// Rate limiting
app.use('/api/', rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: { error: 'Too many requests' }
}));

// Body parsing with size limits
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));

// Data sanitization
app.use(mongoSanitize());
app.use(xss());

// Prevent parameter pollution
app.use(hpp({
  whitelist: ['price', 'rating', 'category']
}));

// Remove X-Powered-By
app.disable('x-powered-by');

Example: Dependency Security Auditing

# Check for known vulnerabilities
npm audit

# Fix automatically
npm audit fix

# Check for outdated packages
npm outdated

# Use Snyk for continuous monitoring
npx snyk test
npx snyk monitor

Expected output:

$ npm audit
# Run  npm install express@4.18.2  to resolve 1 vulnerability
# SEMVER WARNING: Recommended action is a potentially breaking change
┌───────────────┬─────────────────────────────────┐
│ Low           │ Prototype Pollution             │
├───────────────┼─────────────────────────────────┤
│ Package       │ qs                               │
├───────────────┼─────────────────────────────────┤
│ Dependency    │ express > qs                     │
└───────────────┴─────────────────────────────────┘

Common Mistakes

  1. Trusting user input — Never trust any input from the client. Validate, sanitize, and escape everything.
  2. Exposing stack traces in production — Stack traces reveal internal code structure. Return generic 500 errors in production.
  3. Using vulnerable dependencies — Outdated packages with known vulnerabilities are the most common attack vector. Run npm audit regularly.
  4. Weak password hashing — Use bcrypt with cost factor 12 or higher. Never use MD5 or SHA1 for passwords.
  5. Missing rate limiting on auth endpoints — Login endpoints without rate limiting allow brute force attacks. Always rate limit authentication.

Practice Questions

  1. What security headers does Helmet add?
  2. How do you prevent NoSQL injection?
  3. Why should you use parameterized queries?
  4. What is the purpose of CSP headers?
  5. Challenge: Perform a security audit on a sample Express API. Identify and fix: missing Helmet headers, weak CORS configuration, no rate limiting on auth, missing input sanitization, exposed X-Powered-By, and outdated dependencies. Run npm audit and fix vulnerabilities.

FAQ

What is the most common API security vulnerability?

Broken authentication and excessive data exposure are the most common according to the OWASP API Security Top 10.

How do I protect against brute force attacks?

Rate limit login endpoints (5 attempts per 15 minutes), use account lockout after failed attempts, and implement CAPTCHA for repeated failures.

Should I use HTTPS for APIs?

Always. HTTPS encrypts all data in transit, preventing man-in-the-middle attacks. Use Let's Encrypt for free TLS certificates.

What is the difference between sanitization and validation?

Validation rejects invalid data. Sanitization removes dangerous parts from otherwise valid data. Use both.

How do I handle security in a microservices architecture?

Use API gateways for authentication and rate limiting, mutual TLS between services, and service-specific secrets.

Mini Project

Perform a complete security hardening of a Node.js REST API. Implement: Helmet security headers, strict CORS, input sanitization (NoSQL injection, XSS), parameter pollution prevention, rate limiting on all endpoints, strict rate limiting on auth, dependency auditing and fixes, and a security.txt endpoint for vulnerability reporting.

What's Next

Now start the REST API project in the next lesson on Building REST APIs with Node.js.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro