/** * > [!NOTE] * > Install using `bunx shadcn@latest add @remix-utils/middleware-csrf`. * * The CSRF middleware protects your application from Cross-Site Request Forgery attacks by validating that requests originate from trusted sources. * * ```ts * import { createCsrfMiddleware } from "remix-utils/middleware/csrf"; * * export const csrfMiddleware = createCsrfMiddleware(); * ``` * * To use it, you need to add it to the `middleware` array in your `app/root.tsx` file. * * ```ts * import { csrfMiddleware } from "~/middleware/csrf.server"; * export const middleware: Route.MiddlewareFunction[] = [csrfMiddleware]; * ``` * * Now, every non-safe request will be validated against CSRF attacks. * * > [!NOTE] * > If you add this middleware to the root route, it will apply to every route in your application. If your app has API routes that should accept cross-site requests (e.g., for webhooks or third-party integrations), you should move the CSRF middleware to a layout route that wraps only your UI routes, leaving API routes unprotected by CSRF validation. * * The middleware uses the `Sec-Fetch-Site` header to determine request origin. Requests from `same-origin` or `same-site` are automatically allowed. For `cross-site` requests, you can specify trusted origins to allow. * * The request origin is determined by checking (in order): * * 1. The `Origin` header * 2. The `Referer` header * 3. The `request.referrer` property * * ##### Allowing Cross-Site Requests * * You can allow cross-site requests from specific origins using a string: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: "https://trusted.com", * }); * ``` * * Or using a RegExp for pattern matching: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: /\.trusted\.com$/, * }); * ``` * * Or using an array of strings and RegExps: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: ["https://trusted1.com", "https://trusted2.com", /\.trusted\.com$/], * }); * ``` * * Or using a function for dynamic validation: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: (origin, request, context) => origin === "https://trusted.com", * }); * ``` * * The function can also be async: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: async (origin, request, context) => { * return await checkOriginInDatabase(origin); * }, * }); * ``` * * ##### Customizing Safe Methods * * By default, the middleware skips CSRF validation for `GET`, `HEAD`, and `OPTIONS` requests. You can customize this: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * safeMethods: ["GET", "HEAD", "OPTIONS", "POST"], * }); * ``` * * ##### Allowing Missing Origin * * You can allow requests with missing or invalid origin headers: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * origin: "https://trusted.com", * allowMissingOrigin: true, * }); * ``` * * > [!WARNING] * > Enabling `allowMissingOrigin` is high risk. When enabled, requests without a parseable origin (missing `Origin`/`Referer` headers, `Sec-Fetch-Site` header, or `Origin: null`) will bypass origin validation entirely. This can allow attackers to perform cross-site requests in environments that don't send origin headers. Only use this option when you're certain that clients without origin headers are within your trusted boundary, or pair it with an additional CSRF token mechanism. * * ##### Custom Untrusted Request Handler * * You can provide a custom handler for requests that fail CSRF validation: * * ```ts * let csrfMiddleware = createCsrfMiddleware({ * onUntrustedRequest(request, context) { * return new Response("Custom forbidden", { status: 418 }); * }, * }); * ``` * * By default, untrusted requests will receive a 403 Forbidden response. * * @author [Sergio Xalambrí](https://sergiodxa.com) * @module Middleware/CSRF */ import type { MiddlewareFunction, RouterContextProvider } from "react-router"; /** * Creates a CSRF protection middleware that validates requests originate from * trusted sources using the `Sec-Fetch-Site` header. * @param options Configuration options for CSRF validation behavior. * @returns A middleware function that validates requests against CSRF attacks. */ export declare function createCsrfMiddleware(options?: createCsrfMiddleware.Options): MiddlewareFunction; export declare namespace createCsrfMiddleware { /** * HTTP request methods that can be configured as safe (exempt from CSRF * validation). Must be uppercase. */ type RequestMethod = "GET" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "DELETE" | "PATCH"; /** * Static origin matching pattern. Can be a single string, a RegExp, or an * array combining both for matching multiple origins. * * > **Warning:** Avoid using the global flag (`g`) on RegExp patterns. * > The `.test()` method on global regexes is stateful (it updates * > `lastIndex`), which can cause inconsistent matching results across * > requests. */ type OriginMatcher = string | RegExp | ReadonlyArray; /** * Return type for the origin resolver function. * - `true` allows the request * - `false`, `null`, or `undefined` rejects the request */ type OriginResolverResult = boolean | null | undefined; /** * Function type for dynamically validating request origins. * @param origin The origin extracted from the request's Origin header, * Referer header, or referrer property. * @param request The incoming request object. * @param context The router context for accessing app-specific data. * @returns Whether to allow the request. Return `true` to allow, or `false`, * `null`, or `undefined` to reject. Can be async. */ type OriginResolver = (origin: string, request: Request, context: Readonly) => OriginResolverResult | Promise; /** * Origin validation configuration. Can be a static matcher pattern or a * dynamic resolver function for complex validation logic. */ type Origin = OriginMatcher | OriginResolver; /** * Function type for handling requests that fail CSRF validation. * @param request The rejected request object. * @param context The router context for accessing app-specific data. * @returns A Response to send to the client. Can be async. */ type UntrustedRequestHandler = (request: Request, context: Readonly) => Response | Promise; /** * Configuration options for the CSRF middleware. */ interface Options { /** * HTTP methods that bypass CSRF validation. These methods are considered * safe because they should not cause side effects. * Must be uppercase (e.g., "GET", not "get"). * @default ["GET", "HEAD", "OPTIONS"] */ safeMethods?: RequestMethod[]; /** * Trusted origins allowed for cross-site requests. * * When a request has `Sec-Fetch-Site: cross-site`, the middleware checks * the request origin against this configuration: * - String: exact match (case-insensitive) * - RegExp: pattern match against the origin * - Array: matches if any element matches * - Function: custom validation logic with access to request and context * * The origin is extracted from (in order): `Origin` header, `Referer` * header, or `request.referrer` property. * * If not specified, all cross-site requests are rejected. */ origin?: Origin; /** * Whether to allow requests when the origin cannot be determined (missing * `Origin` header, `Referer` header, `Sec-Fetch-Site` header, and * `request.referrer` property) or cannot be parsed as a valid URL. * * > **Warning:** Enabling this option is high risk. Requests without a * > parseable origin will bypass origin validation entirely, which can * > allow attackers to perform cross-site requests in environments that * > don't send origin headers. Only use this when you're certain that * > clients without origin headers are within your trusted boundary, or * > pair it with an additional CSRF token mechanism. * * @default false */ allowMissingOrigin?: boolean; /** * Custom handler for requests that fail CSRF validation. * Use this to log attempts, return custom error responses, or implement * additional security measures. * @param request The rejected request. * @param context The router context. * @returns A Response to send to the client. * @default Throws a 403 Forbidden response. */ onUntrustedRequest?: UntrustedRequestHandler; } }