{"id":10692,"library":"csrf-csrf","title":"Stateless CSRF Protection for Express (Double Submit Cookie)","description":"csrf-csrf is a utility package designed to provide stateless Cross-Site Request Forgery (CSRF) protection for Express applications, implementing the Double Submit Cookie Pattern. Currently at version 4.0.3, it offers a robust alternative to the deprecated `csurf` library, aiming for a simpler and more explicit configuration. Unlike session-based CSRF protection mechanisms like `csrf-sync` (which uses the Synchronizer Token Pattern), `csrf-csrf` is suited for stateless architectures, making it a distinct choice for specific application designs. The library ships with comprehensive TypeScript types (requiring TypeScript >= 3.8) and emphasizes clear implementation guidance to prevent common misconfigurations that can render CSRF protection ineffective. Development is active, with a recent major version release bringing breaking changes and improvements, and it explicitly recommends consulting upgrade guides for migration.","status":"active","version":"4.0.3","language":"javascript","source_language":"en","source_url":"https://github.com/Psifi-Solutions/csrf-csrf","tags":["javascript","csrf","middleware","express","tokens","typescript"],"install":[{"cmd":"npm install csrf-csrf","lang":"bash","label":"npm"},{"cmd":"yarn add csrf-csrf","lang":"bash","label":"yarn"},{"cmd":"pnpm add csrf-csrf","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"`cookie-parser` middleware is required to be registered before `doubleCsrfProtection` to handle HTTP cookies.","package":"cookie-parser","optional":false}],"imports":[{"note":"The `doubleCsrf` function is the primary named export and a factory for the middleware. Version 4+ is ESM-only; CommonJS `require` will result in an `ERR_REQUIRE_ESM` error.","wrong":"const { doubleCsrf } = require('csrf-csrf');","symbol":"doubleCsrf","correct":"import { doubleCsrf } from 'csrf-csrf';"},{"note":"Use `import type` for type-only imports to ensure correct TypeScript inference and prevent bundling issues. This interface defines the configuration options for `doubleCsrf`.","symbol":"DoubleCsrfConfigOptions","correct":"import type { DoubleCsrfConfigOptions } from 'csrf-csrf';"},{"note":"This type augments the Express `Request` object, adding properties like `csrfToken` or methods relevant to CSRF handling, useful for custom type declarations or explicit type hints.","symbol":"CsrfRequest","correct":"import type { CsrfRequest } from 'csrf-csrf';"}],"quickstart":{"code":"import express from 'express';\nimport cookieParser from 'cookie-parser';\nimport { doubleCsrf } from 'csrf-csrf';\nimport { Request, Response } from 'express';\n\nconst app = express();\nconst port = 3000;\n\n// Initialize doubleCsrf with a secret.\n// For production, use a strong, securely generated secret from environment variables.\nconst {\n  doubleCsrfProtection,\n  generateToken,\n} = doubleCsrf({\n  secret: process.env.CSRF_SECRET ?? 'your-very-strong-and-secret-key-that-you-must-change-in-prod-12345',\n  cookieName: 'x-csrf-token',\n  cookieOptions: {\n    httpOnly: true,\n    sameSite: 'lax', // 'Lax' or 'Strict' is recommended for CSRF protection\n    secure: process.env.NODE_ENV === 'production',\n  },\n  getTokenFromRequest: (req: Request) => {\n    // IMPORTANT: Be explicit here. Do not use fallthroughs (||, ??).\n    // Prioritize header, then body. If not found, return an empty string.\n    if (req.headers['x-csrf-token']) {\n      return req.headers['x-csrf-token'] as string;\n    }\n    if (req.body && req.body._csrf) {\n      return req.body._csrf;\n    }\n    return '';\n  },\n});\n\napp.use(express.json()); // For parsing application/json\napp.use(express.urlencoded({ extended: true })); // For parsing application/x-www-form-urlencoded\napp.use(cookieParser(process.env.COOKIE_SECRET ?? 'your-cookie-secret-for-signing-if-needed')); // Use a separate secret for cookie-parser\n\n// CSRF protection middleware must be after cookie-parser\napp.use(doubleCsrfProtection);\n\n// Route to get a new CSRF token for the frontend\napp.get('/csrf-token', (req: Request, res: Response) => {\n  const csrfToken = generateToken(req);\n  res.json({ csrfToken });\n});\n\n// Example protected POST route\napp.post('/submit-data', (req: Request, res: Response) => {\n  // If the doubleCsrfProtection middleware didn't throw an error,\n  // the request is considered valid.\n  res.json({ message: 'Data submitted successfully!', received: req.body });\n});\n\napp.listen(port, () => {\n  console.log(`Server listening on port ${port}`);\n});","lang":"typescript","description":"Demonstrates a basic Express server configuring `csrf-csrf` with `cookie-parser`, exposing a route to fetch a CSRF token, and protecting a POST endpoint. It highlights proper middleware ordering and token retrieval."},"warnings":[{"fix":"Refer to the official upgrade guide (`UPGRADING.md`) and `CHANGELOG.md` for specific changes and adapt your code accordingly.","message":"Version 4 introduces breaking changes. If upgrading from version 3, consult the `CHANGELOG.md` and `UPGRADING.md` guides for detailed migration steps, particularly regarding configuration options and import paths.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Ensure `app.use(cookieParser(...))` is called before `app.use(doubleCsrfProtection)`. If using `express-session`, `cookie-parser` should be after `express-session` as well.","message":"The `cookie-parser` middleware MUST be registered *before* `doubleCsrfProtection`. Incorrect ordering will prevent `csrf-csrf` from accessing and setting the necessary CSRF cookies, leading to validation failures.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement `getTokenFromRequest` with clear, explicit checks, prioritizing trusted locations like a specific header or body field. For example, `if (req.headers['x-csrf-token']) { return req.headers['x-csrf-token']; } else if (req.body._csrf) { return req.body._csrf; } return '';`","message":"The `getTokenFromRequest` configuration option requires explicit logic to retrieve the CSRF token. Avoid using fallthroughs (e.g., `||`, `??`) that might inadvertently accept tokens from multiple, potentially insecure locations, mimicking vulnerabilities found in older CSRF libraries.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Assess your application's state management. If using sessions, consider `csrf-sync`. If strictly stateless, `csrf-csrf` is appropriate, but understand the pattern's implications.","message":"This package implements the Double Submit Cookie Pattern for *stateless* CSRF protection. If your application relies on *sessions*, it is strongly recommended to use `csrf-sync` (Synchronizer Token Pattern) instead, as it is designed for session-based CSRF protection and offers different security considerations.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Store secrets in environment variables (e.g., `process.env.CSRF_SECRET`) and ensure they are sufficiently long and random.","message":"The `secret` provided to `doubleCsrf` (and any secret for `cookie-parser`) should be a strong, unique, and securely managed value, preferably loaded from environment variables. Using a hardcoded or weak secret compromises the integrity of your CSRF protection.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure the frontend sends the CSRF token (obtained from `/csrf-token` endpoint) in the correct header (`x-csrf-token`) or body field (`_csrf`) as configured by `getTokenFromRequest`.","cause":"The client request did not include a valid CSRF token, or `getTokenFromRequest` was unable to find it, or the token provided did not match the one expected by the server.","error":"Error: Invalid or missing CSRF token"},{"fix":"Add `import cookieParser from 'cookie-parser';` (ESM) or `const cookieParser = require('cookie-parser');` (CJS, for versions <4) and `app.use(cookieParser(...))` before `app.use(doubleCsrfProtection)`.","cause":"The `cookie-parser` middleware was not correctly imported or initialized before being used in the Express application.","error":"ReferenceError: cookieParser is not defined"},{"fix":"Provide a string secret to the `doubleCsrf` configuration object: `doubleCsrf({ secret: 'your-strong-secret', ... })`.","cause":"The `secret` option passed to the `doubleCsrf` function is either missing or not of the expected type.","error":"TypeError: secret must be a string or array of strings"},{"fix":"Switch to ES Module syntax: `import { doubleCsrf } from 'csrf-csrf';`. Ensure your `package.json` specifies `\"type\": \"module\"` for your project, or rename files to `.mjs`.","cause":"Attempted to use `require()` to import `csrf-csrf` in a CommonJS module, but the package (version 4+) is exclusively an ES Module.","error":"ERR_REQUIRE_ESM"}],"ecosystem":"npm"}