DEV Community

Cover image for 10 Open-Source Security Tools Every Node.js Developer Should Know
Tommaso Bertocchi
Tommaso Bertocchi

Posted on

10 Open-Source Security Tools Every Node.js Developer Should Know

Your Node.js app is not secure by default.
No security headers, no dependency scanning, no file upload validation.

These 10 packages drop in today — each closing a specific attack vector the framework leaves open.

TL;DR: These 10 tools cover the gap between a working Node.js app and a secure one — all of them free.

Table of Contents

  1. helmet — HTTP security headers in one middleware call
  2. express-rate-limit — stop brute force before it hits your handlers
  3. pompelmi — scan uploaded files before they touch your storage
  4. joi — schema validation that keeps bad input out of your database
  5. snyk — catch vulnerable dependencies before they ship
  6. semgrep — static analysis for security patterns in your code
  7. jsonwebtoken — JWTs with algorithm control that actually matters
  8. dotenv-safe — crash loud when environment variables are missing
  9. audit-ci — block CI builds that introduce known vulnerabilities
  10. lusca — CSRF protection for session-based applications

1) helmet — security headers in one line

What it is: Express middleware setting 11 security HTTP headers — CSP, X-Frame-Options, HSTS — with safe defaults.

Why it matters in 2026: A vanilla Express app sends no security headers. Clickjacking, MIME sniffing, and missing CSP all have header-level mitigations browsers enforce automatically — but only if the server sends them. There's no argument for skipping this.

Best for: All Express applications, any server serving browser clients

Links: helmet preview | Website

GitHub logo helmetjs / helmet

Help secure Express apps with various HTTP headers

Helmet

Helmet helps secure Node/Express apps. It sets HTTP response headers such as Content-Security-Policy and Strict-Transport-Security. It aims to be quick to integrate and be low maintenance afterward.

Quick start

import helmet from "helmet";

const app = express();

app.use(helmet());
Enter fullscreen mode Exit fullscreen mode

This will set 13 HTTP response headers in your app.

Helmet can also be used in standalone Node.js, or with other frameworks.

Configuration

Each header can be disabled. To disable a header:

// Disable the Content-Security-Policy and X-Download-Options headers
app.use(
  helmet({
    contentSecurityPolicy: false,
    xDownloadOptions: false,
  }),
);
Enter fullscreen mode Exit fullscreen mode

To configure a header, pass header-specific options:

// Configure the Content-Security-Policy header.
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        "script-src": ["'self'", "example.com"],
      },
    },
  })
Enter fullscreen mode Exit fullscreen mode

2) express-rate-limit — the simplest fix for brute force

What it is: Express middleware limiting repeated requests per IP with configurable windows and Redis store support.

Why it matters in 2026: Login brute force and credential stuffing require volume. Express ships with no rate limiting. Adding this to auth endpoints takes ten minutes and eliminates a whole class of attacks — pair with Redis and limits work across all instances.

Best for: Login routes, password reset, OTP verification, any sensitive endpoint

Links: express-rate-limit preview

GitHub logo express-rate-limit / express-rate-limit

Basic rate-limiting middleware for the Express web server

express-rate-limit

tests npm version npm downloads license

Basic rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset Plays nice with express-slow-down and ratelimit-header-parser.

Usage

The full documentation is available on-line.

import { rateLimit } from 'express-rate-limit'

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
    standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
    legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
    ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
    // store: ... , // Redis, Memcached, etc. See below.
})

// Apply the rate limiting middleware to all requests.
app.use(limiter)
Enter fullscreen mode Exit fullscreen mode

Data Stores

The…


3) pompelmi — file upload scanning that runs on your server

What it is: A minimal Node.js wrapper around ClamAV that scans any uploaded file and returns a typed verdict — Clean, Malicious, or ScanError. Zero runtime dependencies, no cloud, no daemon required.

Why it matters in 2026: File uploads are the most underdefended attack surface in most Node.js apps. A user uploads — your code saves it without checking the content. pompelmi adds antivirus scanning as a middleware step, with no data leaving your server.

Best for: Express, Fastify, NestJS, Next.js, SvelteKit, any Node.js app that accepts user file uploads

Links: pompelmi preview | Website

GitHub logo pompelmi / pompelmi

Minimal Node.js wrapper around ClamAV — scan any file and get Clean, Malicious, or ScanError. Handles installation and database updates automatically.

pompelmi logo

pompelmi

ClamAV for humans

npm version license platform zero dependencies


A minimal Node.js wrapper around ClamAV that scans any file and returns a typed Verdict Symbol: Verdict.Clean, Verdict.Malicious, or Verdict.ScanError. No daemons. No cloud. No native bindings. Zero runtime dependencies.

Table of contents



Quickstart

npm install pompelmi
Enter fullscreen mode Exit fullscreen mode
const { scan, Verdict } = require('pompelmi');

const result = await scan('/path/to/file.zip');

if (result === Verdict.Malicious) {
  throw new Error('File rejected: malware detected');
}
Enter fullscreen mode Exit fullscreen mode

How it works

  1. Validate — pompelmi checks that the argument is a string and that the file exists before spawning anything.
  2. Scan — pompelmi spawns clamscan --no-summary <filePath> as a child process and reads the exit code.
  3. Map — the exit…




developer realizing they never validated uploaded files

Your users uploading files to your unprotected endpoint. Source: Giphy


4) joi — server-side validation that doesn't trust the client

What it is: A schema description language and data validator that validates request payloads at runtime against defined shapes and constraints.

Why it matters in 2026: TypeScript types are a compile-time promise — at runtime your API receives whatever the client sends. Joi validates shape, type, and constraints before data reaches business logic. It's the boundary guard your types can't be.

Best for: API request validation, form data, webhook payloads, any externally supplied data

Links: joi preview | Website

GitHub logo hapijs / joi

The most powerful data validation library for JS





5) snyk — dependency vulnerability scanning in CI

What it is: A CLI scanning package.json and lockfiles against a curated vulnerability database, integrating with GitHub and CI systems.

Why it matters in 2026: Transitive dependencies introduce CVEs silently. npm audit has a high false positive rate; Snyk's curated database integrates into CI, blocking deploys on high-severity findings.

Best for: CI pipelines, pre-commit hooks, scheduled production dependency scans

Links: cli preview | Website

GitHub logo snyk / cli

Snyk CLI scans and monitors your projects for security vulnerabilities.

Getting started with the Snyk CLI

Introduction to the Snyk CLI

Snyk is a developer-first, cloud-native security tool to scan and monitor your software development projects for security vulnerabilities. Snyk scans multiple content types for security issues:

  • Snyk Open Source: Find and automatically fix open-source vulnerabilities
  • Snyk Code: Find and fix vulnerabilities in your application code in real time
  • Snyk Container: Find and fix vulnerabilities in container images and Kubernetes applications
  • Snyk IaC: Find and fix insecure configurations in Terraform and Kubernetes code

The Snyk CLI brings the functionality of Snyk into your development workflow. You can run the CLI locally from the command line or in an IDE. You can also run the CLI in your CI/CD pipeline. The following shows an example of Snyk CLI test command output.

Snyk CLI test command output example

Snyk CLI scanning supports many languages and tools. For detailed…


6) semgrep — static analysis for security patterns

What it is: A static analysis engine using pattern-matching rules to find vulnerabilities in source code without executing it.

Why it matters in 2026: Dynamic testing finds reachable vulnerabilities. Static analysis finds ones in untested code paths — Semgrep's Node.js rules catch SQL injection, prototype pollution, and unsafe eval in seconds.

Best for: Pre-commit hooks, CI pipelines, finding injection vulnerabilities before they ship

Links: semgrep preview | Website

GitHub logo semgrep / semgrep

Lightweight static analysis for many languages. Find bug variants with patterns that look like source code.


Semgrep logo

Code scanning at ludicrous speed

Homebrew PyPI Documentation Join Semgrep community Slack Issues welcome! Star Semgrep on GitHub Docker Pulls Docker Pulls (Old) Follow @semgrep on Twitter

Semgrep is a fast, open-source, static analysis tool that searches code, finds bugs, and enforces secure guardrails and coding standards. Semgrep supports 30+ languages and can run in an IDE, as a pre-commit check, and as part of CI/CD workflows.

Semgrep is semantic grep for code. While running grep "2" would only match the exact string 2, Semgrep would match x = 1; y = x + 1 when searching for 2. Semgrep rules look like the code you already write; no abstract syntax trees, regex wrestling, or painful DSLs.

Note that in security contexts, Semgrep Community Edition will miss many true positives as it can only analyze code within the boundaries of a single function or file. If you want to use Semgrep for security purposes (SAST, SCA, or secrets scanning), the Semgrep AppSec Platform is strongly recommended…




developer finding a security bug before it ships

Finding an SQL injection in CI vs finding it in a pentest report. Source: Giphy


7) jsonwebtoken — JWTs with algorithm control

What it is: The standard Node.js library for signing and verifying JSON Web Tokens with explicit algorithm control.

Why it matters in 2026: JWT libraries have a history of alg: none attacks accepting unsigned tokens. jsonwebtoken handles this correctly by default and gives you explicit algorithm control.

Best for: Authentication systems, API token issuance, microservice-to-microservice trust

Links: node-jsonwebtoken preview

GitHub logo auth0 / node-jsonwebtoken

JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html

jsonwebtoken

Build Dependency
Build Status Dependency Status

An implementation of JSON Web Tokens.

This was developed against draft-ietf-oauth-json-web-token-08. It makes use of node-jws

Install

$ npm install jsonwebtoken
Enter fullscreen mode Exit fullscreen mode

Migration notes

Usage

jwt.sign(payload, secretOrPrivateKey, [options, callback])

(Asynchronous) If a callback is supplied, the callback is called with the err or the JWT.

(Synchronous) Returns the JsonWebToken as string

payload could be an object literal, buffer or string representing valid JSON.

Please note that exp or any other claim is only set if the payload is an object literal. Buffer or string payloads are not checked for JSON validity.

If payload is not a buffer or a string, it will be coerced into a string using JSON.stringify.

secretOrPrivateKey is a string (utf-8 encoded), buffer, object, or KeyObject containing either the secret for HMAC algorithms or the PEM encoded private key for RSA and ECDSA. In…


8) dotenv-safe — environment validation that fails loud

What it is: A drop-in dotenv replacement requiring all variables in .env.example to be present at startup.

Why it matters in 2026: An app missing DATABASE_URL doesn't fail at boot — it fails 500ms later with a confusing error buried in a stack trace. dotenv-safe moves this failure to startup where it's obvious and actionable.

Best for: Node.js apps with external dependencies, CI environments, Docker deployments

Links: dotenv-safe preview

GitHub logo rolodato / dotenv-safe

Load environment variables from .env and ensure they are all present

dotenv-safe Build Status

Identical to dotenv, but ensures that all needed environment variables are defined after reading from .env The names of the needed variables are read from .env.example, which should be commited along with your project.

dotenv-safe only checks if all the needed variable names exist in process.env after initialising. It does not assume anything about the presence, format or validity of the values.

Installation

npm install dotenv-safe
pnpm install dotenv-safe
yarn add dotenv-safe

Example

# .env.example, committed to repo
SECRET=
TOKEN=
KEY=
Enter fullscreen mode Exit fullscreen mode
# .env, private
SECRET=topsecret
TOKEN=
Enter fullscreen mode Exit fullscreen mode
// index.js
require('dotenv-safe').config();
Enter fullscreen mode Exit fullscreen mode

Or, if you are using ES modules:

// index.mjs
import { config } from 'dotenv-safe';
config();
Enter fullscreen mode Exit fullscreen mode

Since the provided .env file does not contain all the variables defined in .env.example, an exception is thrown:

MissingEnvVarsError: The following variables

9) audit-ci — CI gate for known vulnerabilities

What it is: A CLI running npm audit with configurable severity thresholds that fails CI builds when new vulnerabilities are introduced.

Why it matters in 2026: npm audit locally is optional. audit-ci in CI makes it mandatory. Allowlist support handles false positives, configurable severity gates block only what matters, and it works with npm, Yarn, and pnpm.

Best for: GitHub Actions, GitLab CI, any pipeline where dependency security is a requirement

Links: audit-ci preview

GitHub logo IBM / audit-ci

Audit NPM, Yarn, PNPM, and Bun dependencies in continuous integration environments, preventing integration if vulnerabilities are found at or above a configurable threshold while ignoring allowlisted advisories

audit-ci

npm version CircleCI GitHub CI CodeQL

This module is intended to be consumed by your favourite continuous integration tool to halt execution if npm audit, yarn audit, or pnpm audit finds vulnerabilities at or above the specified threshold while ignoring allowlisted advisories.

Note: Use our codemod to update to audit-ci v6.0.0

Requirements

  • Node >=16
  • (Optional) Yarn ^1.12.3 || Yarn >=2.4.0 && <4.0.0
  • (Optional) PNPM >=4.3.0
  • (Optional) Bun

Limitations

  • Yarn Classic workspaces does not audit devDependencies. See this issue for more information.
  • Yarn v4 is not supported because it provides similar functionality to audit-ci. For more information, see the documentation on yarn npm audit. If you'd like audit-ci to support Yarn v4, voice your opinion on this issue.
  • Bun is supported by exporting the bun.lockb into a Yarn v1 yarn.lock file. Accordingly, auditing a bun.lockb file with audit-ci requires Yarn v1.

Set up

(Recommended) Install audit-ci during your CI environment…


10) lusca — CSRF protection for session-based apps

What it is: Express middleware for CSRF token validation supporting synchronizer token and double-submit cookie patterns.

Why it matters in 2026: CSRF is declared dead every time SPAs are discussed. It isn't — not for apps with session cookies, form submissions, or state-changing operations without custom headers. Lusca handles the full CSRF lifecycle in a few lines of configuration.

Best for: Server-rendered Express apps, session-based auth, forms that mutate state

Links: lusca preview

GitHub logo krakenjs / lusca

Application security for express apps.

lusca

Build Status NPM version

Web application security middleware.

Usage

var express = require('express'),
    app = express(),
    session = require('express-session'),
    lusca = require('lusca');

//this or other session management will be required
app.use(session({
    secret: 'abc',
    resave: true,
    saveUninitialized: true
}));

app.use(lusca({
    csrf: true,
    csp: { /* ... */},
    xframe: 'SAMEORIGIN',
    p3p: 'ABCDEF',
    hsts: {maxAge: 31536000, includeSubDomains: true, preload: true},
    xssProtection: true,
    nosniff: true,
    referrerPolicy: 'same-origin'
}));
Enter fullscreen mode Exit fullscreen mode

Setting any value to false will disable it. Alternately, you can opt into methods one by one:

app.use(lusca.csrf());
Enter fullscreen mode Exit fullscreen mode

Final thoughts

Most Node.js security incidents are boring gaps: no rate limiting, no headers, no scanning. These 10 tools close each one. What's missing from your stack?

Top comments (0)