npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

small-csrf

v1.1.1

Published

A small, simple CSRF protection middleware for Express

Readme

small-csrf

A lightweight CSRF protection middleware for Express applications implementing OWASP's Signed Double-Submit Cookie pattern.

Introduction

small-csrf provides robust Cross-Site Request Forgery (CSRF) protection for your Node.js/Express applications. It implements the OWASP Signed Double-Submit Cookie pattern, which binds CSRF tokens to user sessions using HMAC signatures for enhanced security.

Key features:

  • Simple integration with Express and express-session
  • Configurable token management
  • Support for both per-session and per-request tokens
  • Constant-time token comparison to prevent timing attacks
  • Flexible configuration options for cookies and tokens

Whilst any implementation errors are my own, credit goes to OWASP and their CSRF Cheat sheet which lays out how they think this should be done.

Installation

npm install small-csrf

Quick Start

Basic integration with Express and express-session:

// npm install express express-session cookie-parser small-csrf
import express from "express";
import session from "express-session";
import cookieParser from "cookie-parser";
import csrfProtection from "small-csrf";

const app = express();

app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());

// session middleware (required for csrf)
app.use(
  session({
    secret: "your-session-secret",
    resave: false,
    saveUninitialized: false,
    cookie: { secure: process.env.NODE_ENV === "production" },
  })
);

// CSRF protection middleware
app.use(
  csrfProtection({
    secret: "at-least-32-characters-long-csrf-secret",
  })
);

// session initialisation middleware 
app.use((req, res, next) => {
  // Ensure session is initialised for session memory store
  if (!req.session.initialized) {
    req.session.initialized = true;
  }
  next();
});

// render a form with CSRF token
app.get("/form", (req, res) => {
  res.send(`
    <form action="/submit" method="POST">
      <input type="hidden" name="_csrf" value="${req.csrfToken()}">
      <input type="text" name="data">
      <button type="submit">Submit</button>
    </form>
  `);
});

app.post("/submit", (req, res) => {
  // if the request reaches here, CSRF validation passed since
  // it's done in the middleware
  res.send("Form submitted successfully!");
});

// handler for CSRF errors
app.use((err, req, res, next) => {
  if (err.code === "EBADCSRFTOKEN") {
    return res.status(403).send("Invalid CSRF token. Form submission failed.");
  }
  next(err);
});

app.listen(3000);

How It Works

small-csrf implements the Signed Double-Submit Cookie pattern as recommended by OWASP:

  1. A cryptographically strong random token is generated per session (by default; see below)
  2. The token is:
    • Set as an HTTP-only cookie with appropriate security settings
    • Made available for inclusion in forms or AJAX requests via req.csrfToken()
  3. When processing state-changing requests (POST, PUT, DELETE), the middleware:
    • Verifies that the token from the cookie matches the token submitted in the request
    • Uses an HMAC signature to bind the token to the user's session
    • Performs validation using constant-time comparison to prevent timing attacks

Token Strategies

Per-Session Tokens (default - OWASP Recommended)

  • One token generated per user session
  • Allows multiple simultaneous form submissions from different tabs
  • Aligns with OWASP's Signed Double-Submit Cookie recommendations
  • More user-friendly for multi-tab browsing

Per-Request Tokens

  • New token generated for each request
  • May cause issues with multiple tabs or back/forward navigation
  • Can be enabled with perSessionTokens: false during initialisation

API Reference

csrfProtection(options)

Creates and returns the CSRF middleware function.

Options

| Option | Type | Default | Description | | ------------------ | ------- | ---------------------------- | ------------------------------------------------------------------- | | secret | String | required | Secret key used for HMAC signature (must be at least 32 characters) | | cookie.key | String | "csrf_token" | Name of the cookie storing the CSRF token | | cookie.path | String | "/" | Path for the CSRF cookie | | cookie.httpOnly | Boolean | true | Whether the cookie is HTTP only | | cookie.sameSite | String | "strict" | SameSite policy for the cookie ("strict", "lax", or "none") | | cookie.secure | Boolean | true | Whether the cookie requires HTTPS | | cookie.maxAge | Number | 3600000 | Max age of the cookie in milliseconds (1 hour default) | | ignoreMethods | Array | ["GET", "HEAD", "OPTIONS"] | HTTP methods that don't need CSRF validation | | csrfParam | String | "_csrf" | Name of the parameter containing the CSRF token in requests | | perSessionTokens | Boolean | true | If true, generates one token per session instead of per request |

req.csrfToken()

Function added to the request object that returns the current CSRF token. Use this to include the token in your forms or AJAX requests.

Security Considerations

For maximum security:

  • Always use HTTPS in production environments
  • Keep your CSRF secret different from your session secret
  • Use a cryptographically strong secret (at least 32 characters)
  • Set appropriate sameSite and secure cookie options based on your application's requirements

Examples

AJAX Requests

When making AJAX requests, include the CSRF token in the header:

// Client-side JavaScript
const csrfToken = document
  .querySelector('meta[name="csrf-token"]')
  .getAttribute("content");

fetch("/api/data", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-Token": csrfToken,
  },
  body: JSON.stringify({ data: "example" }),
});

Server-side template (example with EJS):

<head>
  <meta name="csrf-token" content="<%= csrfToken() %>" />
</head>

Single Page Applications (SPAs)

For SPAs, you can expose the CSRF token through an API endpoint:

app.get("/api/csrf-token", (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

Then fetch the token when your SPA initialises.

Contributing

Contributions and issues are welcome, especially for security issues.

Tests

Uses the built-in Node test runner - available from Node 20 npm test to run

Example App

To run a local demo of small-csrf in an Express app from a cloned repo:

cd example
npm install
npm start

License

MIT

Versions

  • 1.0.0 - initial
  • 1.0.1 - removed query parameters
  • 1.1.0 - change default to per-session tokens
  • 1.1.1 - GitHub Action for secure NPM publishing