From fe5aee9924bc84ee1a95d5b5cc4bfbcd960bd724 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:12:25 +0330 Subject: [PATCH] Import source: backend/src/middleware/csrf Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- backend/src/middleware/csrf.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 backend/src/middleware/csrf.ts diff --git a/backend/src/middleware/csrf.ts b/backend/src/middleware/csrf.ts new file mode 100644 index 0000000..34c5f75 --- /dev/null +++ b/backend/src/middleware/csrf.ts @@ -0,0 +1,34 @@ +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(); +};