import type { RequestHandler } from 'express'; import { env } from '../config/env.js'; import { HttpError } from './errorHandler.js'; /** * Strict origin-based CSRF protection for mutating HTTP requests. * Compares request Origin/Referer against the configured frontendUrl. */ export const csrfProtection: RequestHandler = (req, _res, next) => { // Safe methods do not modify state and are exempt from CSRF checks if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } const origin = (req.headers.origin || req.headers.referer) as string; if (!origin) { // If no origin/referer header is sent (e.g. direct curl/scripts), we permit it // because CSRF requires a browser to automatically attach credentials (cookies) // on cross-origin requests. Direct API consumers sending keys/tokens are not susceptible to CSRF. return next(); } try { const originUrl = new URL(origin); const frontendUrl = new URL(env.frontendUrl); if (originUrl.host !== frontendUrl.host) { throw new HttpError(403, 'CSRF_DETECTED', 'Security policy violation: cross-site request blocked'); } } catch { throw new HttpError(403, 'CSRF_DETECTED', 'Security policy violation: malformed origin identifier'); } next(); };