Skip to content

CSRF protection uses a hardcoded fallback secret: tokens are forgeable when CSRF_SECRET is unset #817

Description

@nanaf6203-bit

CSRF protection uses a hardcoded fallback secret: tokens are forgeable when CSRF_SECRET is unset

Labels / Complexity: bug · security · High Complexity — High

Problem

src/lib/csrf.ts line 4 derives the CSRF secret from the environment with a known fallback:

const CSRF_SECRET = process.env.CSRF_SECRET || 'default-fallback-csrf-secret-key-32-chars-long!';

The fallback string is committed to the repository, so in any deployment where CSRF_SECRET is unset, every CSRF token is signed with a publicly-known key. An attacker can forge valid CSRF tokens for any state-changing request the app protects with this module — in a deployment without the env var, the CSRF defense is equivalent to not having it. This is the same defect class previously fixed elsewhere in the stack: the BackEnd rejected unset secrets for JWT_SECRET (closed #663/#892) and the frontend's middleware.ts still has its own AUTH_SECRET fallback (separate issue) — but csrf.ts is a distinct, still-live instance with its own secret.

Root cause

src/lib/csrf.ts line 4: the || 'default-fallback-csrf-secret-key-32-chars-long!' fallback.

Why this is architecturally hard

  1. The failure mode must be fail-closed. The fix cannot silently disable CSRF when the secret is missing; the module must refuse to mint/verify tokens (or the app must fail at build/startup via scripts/validate-env.js, which currently does not require CSRF_SECRET). The contributor must pick the enforcement point and keep the token-verification flow intact.
  2. Deployments must be updated in the same change. Because the current code has worked (insecurely) without the env var, making it required will break environments that never set it; the issue should note updating the env example/docs so operators set CSRF_SECRET before the enforcement lands.

Acceptance criteria

  • With CSRF_SECRET unset, token minting/verification fails closed (no tokens issued, or a clear error), and the literal fallback string is gone from src/lib/csrf.ts.
  • With CSRF_SECRET set, current behavior is unchanged (tokens mint and verify).
  • A test proves a token signed with the old fallback string is rejected when the real secret is configured.
  • scripts/validate-env.js (or the equivalent env documentation) requires CSRF_SECRET.
  • npm run typecheck, npm test, and npm run lint pass.

Out of scope

Replacing the CSRF token scheme (e.g. same-site cookies vs. double-submit) is out of scope.

Getting started

  • src/lib/csrf.ts — the fallback at line 4 and the mint/verify functions
  • scripts/validate-env.js — where the env var should be required
  • src/lib/__tests__/ — check for existing csrf tests to extend

Commands: npm run typecheck, npm test, npm run lint.

Good first files to read: src/lib/csrf.ts, scripts/validate-env.js.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

High ComplexityStellar WaveIssues in the Stellar wave programbugSomething isn't working correctlysecuritySecurity issue or hardening opportunity

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions