A utility package to help implement stateless CSRF protection using the Double Submit Cookie Pattern in express.

Overview

Double CSRF

A utility package to help implement stateless CSRF protection using the Double Submit Cookie Pattern in express.

Dos and Don'tsGetting StartedConfigurationSupport

Background

This module provides the necessary pieces required to implement CSRF protection using the Double Submit Cookie Pattern. This is a stateless CSRF protection pattern, if you are using sessions and would prefer a stateful CSRF strategy, please see csrf-sync for the Synchroniser Token Pattern.

Since csurf has been deprecated I struggled to find alternative solutions that were accurately implemented and configurable, so I decided to write my own! Thanks to NextAuth as I referenced their implementation. From experience CSRF protection libraries tend to complicate their configuration, and if misconfigured, can render the protection completely useless.

This is why csrf-csrf aims to provide a simple and targeted implementation to simplify it's use.

Dos and Don'ts

  • Do read the OWASP - Cross-Site Request Forgery Prevention Cheat Sheet
  • Do read the OWASP - Secrets Management Cheat Sheet
  • Do follow the recommendations when configuring csrf-double.
  • Do join the Discord server and ask questions in the psifi-support channel if you need help.
  • Do follow fastify/csrf-protection recommendations for secret security.
  • Do keep secure and signed as true in production.
  • Do make sure you do not compromise your security by not following best practices.
  • Do not use the same secret for csrf-csrf and cookie-parser.
  • Do not transmit your CSRF token by cookies.
  • Do not expose your CSRF tokens or hash in any log output or transactions other than the CSRF exchange.
  • Do not transmit the token hash by any other means.

Getting Started

This section will guide you through using the default setup, which does sufficiently implement the Double Submit Cookie Pattern. If you'd like to customise the configuration, see the configuration section.

You will need to be using cookie-parser and the middleware should be registered before Double CSRF. This utility will set a cookie containing a hash of the csrf token and provide the non-hashed csrf token so you can include it within your response.

npm install cookie-parser csrf-csrf
// ESM
import { csrfSync } from "csrf-sync";

// CommonJS
const { csrfSync } = require("csrf-sync");
const {
  invalidCsrfTokenError, // This is just for convenience if you plan on making your own middleware.
  generateToken, // Use this in your routes to provide a CSRF hash cookie and token.
  validateRequest, // Also a convenience if you plan on making your own middleware.
  doubleCsrfProtection, // This is the default CSRF protection middleware.
} = doubleCsrf(doubleCsrfOptions);

This will extract the default utilities, you can configure these and re-export them from your own module. You should only transmit your token to the frontend as part of a response payload, do not include the token in response headers or in a cookie, and do not transmit the token hash by any other means.

To create a route which generates a CSRF token and hash cookie:

const myRoute = (request, response) => {
  const csrfToken = generateToken(response);
  // You could also pass the token into the context of a HTML response.
  res.json({ csrfToken });
};
const myProtectedRoute = (req, res) =>
  res.json({ unpopularOpinion: "Game of Thrones was amazing" });

You can also put the token into the context of a templated HTML response. Just make sure you register this route before registering the middleware so you don't block yourself from getting a token.

// Make sure your session middleware is registered before these
express.use(session);
express.get("/csrf-token", myRoute);
express.use(doubleCsrfProtection);
// Any non GET routes registered after this will be considered "protected"

By default, any request that are not GET, HEAD, or OPTIONS methods will be protected. You can configure this with the ignoredMethods option.

You can also protect routes on a case-to-case basis:

app.get("/secret-stuff", doubleCsrfProtection, myProtectedRoute);

Once a route is protected, you will need to ensure the hash cookie is sent along with the request and by default you will need to include the generated token in the x-csrf-token header, otherwise you'll receive a `403 - ForbiddenError: invalid csrf token`. If your cookie is not being included in your requests be sure to check your withCredentials and CORS configuration.

Configuration

When creating your csrfSync, you have a few options available for configuration, the only required option is getSecret, the rest have sensible defaults (shown below).

const doubleCsrfUtilities = doubleCsrf({
  getSecret, // A function that optionally takes the request and returns a secret
  cookieName = "__Host-psifi.x-csrf-token", // The name of the cookie to be used, recommend using Host prefix.
  cookieOptions: {
    httpOnly = true,
    sameSite = "lax",  // Recommend you make this strict if posible
    path = "/",
    secure = true,
    ...remainingCOokieOptions // Additional options supported: domain, maxAge, expires
  } = {},
  size = 64, // The size of the generated tokens in bits
  ignoredMethods = ["GET", "HEAD", "OPTIONS"], // A list of request methods that will not be protected.
  getTokenFromRequest = (req) => req.headers["x-csrf-token"], // A function that returns the token from the request
}:

Sessions

If you plan on using express-session then please ensure your cookie-parser middleware is registered after express-session, as express session parses it's own cookies and may cionflict.

getSecret

(request: Request) => string;

This should return a secret key for hashing, using a hard coded string return works:

() => "my key";

However it is highly recommend you implement some rotating secret key so that tokens become invalidated after a certain period of time. For example, you could use sessions, or some server side state attached to the request (via middleware). You could then have some external means of updating and rotating what your getSecret returns and you could then use that:

(req) => req.secret;
// or
(req) => req.session.secret;
// or some other externally rotated secret key

Support

  • Join the Discord and ask for help in the psifi-support channel.
  • Pledge your support through the Patreon
Comments
  • feat: introducing a new example (client and server code)

    feat: introducing a new example (client and server code)

    Hi @psibean. I added one example more. It is in the "complete" folder, and I moved your example to the "simple" folder. I hope this helps to some people who wants to use your library.

    opened by cr0wg4n 10
  • CommonJS usage

    CommonJS usage

    Would it be possible to provide a CommonJS output alongside ESM?

    With the deprecation of csurf, I was planning on using this with NestJS but as it uses CommonJS it causes some require issues.

    enhancement 
    opened by SeanLatimer 8
  • Signed cookie in generateToken fn

    Signed cookie in generateToken fn

    Not sure but if I will try to follow CSRF in Angular like here then if I will configure csrf-csrf with this config:

    const config = {
      cookieName: 'x-csrf-token-hash'
      options: {
        // ...
        signed: true
      }
    }
    
    const { generateToken, validateReqeust } = doubleCsrf(config);
    

    I guess that when I'll use generateToken then this signed flag should be respected (please correct me if I'm wrong)

    // line 31 in index
    res.cookie(cookieName, csrfTokenHash, cookieOptions)
    

    And at the end return value should be assigned to res.cookie['x-csrf-token'] cause atm we have only token hash assigned to cookies

    WDYT? Any explanation is more than welcome ;>

    opened by Dzixxx 2
  • feat: support rotating and dynamic secrets

    feat: support rotating and dynamic secrets

    Turns out it's just easier to replace secret with getSecret - rotating secrets should be a preferred (and recommended / encouraged) option. So supporting a single string via () => string as a secondary is just a convenience.

    The benefit of being able to rotate a secret allows for invalidating a previously generated token. If you only ever have a single secret, all tokens and their respective hash will always be valid, unless you also implement your own server side tracking.

    If you introduce sessions, you can rotate the secret per session, using different secrets for unauthenticated vs authenticated sessions.

    breaking change for issue #3

    opened by psibean 0
  • Support for secret rotation

    Support for secret rotation

    Currently the protection takes a single secret and uses it indefinitely.

    What I would like to do is have a getSecret function, the option described herein would be backwards compatible, so only a minor change. Edit: Nevermind, type changes may require a major change as per: https://www.semver-ts.org/

    export type CsrfSecretRetriver = (req?: Request) => string;
    

    which will just default to:

    () => secret
    

    We can use this type:

    export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
        Pick<T, Exclude<keyof T, Keys>> 
        & {
            [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
        }[Keys]
    

    to ensure that at least one of "secret" and "getSecret" are passed to the function. And have something like this:

     if (typeof secret !== 'string' && typeof remainingDoubleCsrfOptions.getSecret !== 'function') {
        throw new Error('Double CSRF invalid configuration: secret must be a string, or getSecret must be a function');
      }
    
      const { getSecret = () => secret as string } = remainingDoubleCsrfOptions;
    

    This also means that the generateTokenAndHash function will need to take in the request parameter, but ONLY when getSecret has been provided in the config. Some additional function overloads can potentially allow for this type inference.

    Being able to rotate your secrets, and even generate your secrets dynamically means you can invalidate older CSRF tokens. and potentially even tie them to a user session if you wish to have some state. The benefit of this is, you can use different secrets between unauthenticated and authenticated sessions as well.

    feature breaking 
    opened by psibean 0
Owner
Psifi Solutions
Psifi Solutions
esse bot envia sinais, do gamer double blaze, direto para chats do telegram. leave the star ⭐️

Bot Blaze Double A blaze.com, site de aposta online, operada pela empresa Prolific Trade N.V. e bastante popular nas mídias sociais. Em um de seus jog

Elizandro Dantas 42 Dec 30, 2022
Functional Programming with NestJS, Prisma. immutable, pure, stateless

Functional-NestJS Functional Programming with NestJS, Prisma. immutable, pure, stateless. 1. Introduction A production ready typescript backend reposi

y0on2q 40 Dec 6, 2022
Multi-chain sniper bot to buy and sell tokens on ETH compatible chains. Features include instant or mempool sniping, rug protection, and sell management.

An open-source defi sniper. defi-sniper is free to download. NEW Community telegram group: https://t.me/+aBLUmP1UnypiNTVh Premium Services Now Availab

spacemonk 6 May 3, 2022
AntiRaid (protection) system in JavaScript..

Basic Discord AntiRaid Protection A protection system against "RAID", a very widespread threat on Discord. - ?? Technologies : JavaScript, Node.JS, Qu

Zitiixou 13 May 28, 2022
A simple Prometheus (aggregated) push gateway allowing stateless/serverless workloads, ephemeral and batch jobs to easily expose their metrics.

Serverless Prometheus (aggregated) Push Gateway A simple Prometheus (aggregated) push gateway allowing stateless/serverless workloads, ephemeral and b

Adam Janiš 17 Dec 4, 2022
More than a Password Protection and Management tool, it secures all your valuable digital assets in your own vault

ZeroPass Client ZeroPass is more than a Password Protection and Management tool, it secures all your valuable digital assets in your own vault, includ

null 6 Aug 22, 2022
e-ONG, an authorial project, whose objective is to help ONGs to find people who need help or would like to help them

This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: npm start Runs the app in the developmen

Lucas Lima 2 Nov 11, 2022
Package fetcher is a bot messenger which gather npm packages by uploading either a json file (package.json) or a picture representing package.json. To continue...

package-fetcher Ce projet contient un boilerplate pour un bot messenger et l'executable Windows ngrok qui va permettre de créer un tunnel https pour c

AILI Fida Aliotti Christino 2 Mar 29, 2022
Connect your Ethereum smart contract to any real world API using the oracle pattern!

Minimal Viable Oracle (MVO) - An effective way to Build your own oracle with Solidity Smart contracts cannot access off-chain data directly. This repo

Noah 9 Aug 25, 2022
MERN authentication using JWT and HTTP-Only cookie

MERN Authentication Starter This is a starter app for a MERN stack application with authentication. This is for a SPA (Single Page Application) workfl

Brad Traversy 133 Aug 13, 2023
Cypress commands are asynchronous. It's a common pattern to use a then callback to get the value of a cypress command

cypress-thenify Rationale Cypress commands are asynchronous. It's a common pattern to use a then callback to get the value of a cypress command. Howev

Mikhail Bolotov 15 Oct 2, 2022
Implementação do Observer Pattern em TypeScript para o Código Fonte TV

Observer - Design Pattern Exemplos de implementação do Design Pattern Observer, descrito no livro Design Patterns: Elements of Reusable Object-Oriente

Gabriel Froes 11 Nov 30, 2022
Ethereum smart contract gas cost waste pattern detection and patching tool

Ethereum smart contract gas cost waste pattern detection and patching tool

ibelab 4 Mar 23, 2022
Rename image after pasting, support name pattern and auto renaming.

Obsidian paste image rename This plugin is inspired by Zettlr, Zettlr shows a prompt that allows the user to rename the image, this is a great help if

Xiao Meng 82 Jan 2, 2023
An algorithm for fast 2D pattern-matching with wildcards.

pattern-match-2d.js An algorithm for fast 2D pattern-matching with wildcards, with a demo app inspired by MarkovJunior (by Maxim Gumin). The algorithm

null 16 Nov 5, 2022
A probabilistic programming language based on pattern-rewriting

MJr-compiler MJr is a probabilistic programming language based on pattern-rewriting, heavily inspired by MarkovJunior by Maxim Gumin. This project pro

Andrew Kay 35 Dec 15, 2022
Transactional Inbox/Outbox pattern for Durable Objects

do-transactional-outbox One of the challenges that many event-driven systems face is the fact that they have to write to the database and send out an

Erwin van der Koogh 5 Sep 27, 2022
An npm package with Tailwind CSS utility classes for creating responsive grid columns without media queries using auto fit.

Grid Auto Fit for Tailwind CSS A plugin that helps you create a responsive grid layout in Tailwind CSS without using media queries. It uses the auto-f

Thirus 80 Dec 28, 2022