SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Architecture / 3 MIN READ

CSRF Defenses for Cookie-Authenticated JSON APIs

Choose layered CSRF controls from browser request behavior, cookie scope, content types, CORS, and the cost of rejecting legitimate clients.

A cookie-authenticated JSON API still needs an explicit CSRF design. SameSite cookies reduce exposure, but they do not make every deployment, browser flow, or sibling subdomain safe. Require a non-simple request carrying an unpredictable CSRF token or a server-approved custom header, validate request origin, and reject form-compatible content types on JSON-only mutation endpoints.

Begin with the browser’s authority

CSRF works because a browser may attach ambient credentials to a request initiated by an attacker-controlled page. The attacker often cannot read the response, but a state change may already have happened. OWASP’s CSRF attack description lays out this confused-deputy shape: an authenticated browser is induced to submit an unwanted action. JSON response bodies do not change the request side of that model.

Inventory every state-changing endpoint and the content types it actually accepts. A handler documented as JSON may also parse application/x-www-form-urlencoded, multipart/form-data, or text/plain. Those formats can make cross-site form submission possible. Return 415 Unsupported Media Type for anything outside the contract before touching state. Keep GET, HEAD, and OPTIONS free of state changes.

Layer controls with different failure modes

Set cookies Secure, HttpOnly where JavaScript does not need them, and SameSite=Lax or Strict when the user experience permits. Host-only cookies narrow exposure compared with a broad parent-domain cookie. Test real redirects, embedded contexts, external identity-provider returns, and legacy clients before tightening behavior.

For browser mutations, use a synchronizer token stored server-side or a signed double-submit pattern implemented carefully. Put the token in a custom request header and compare it using constant-time logic where applicable. A custom header also forces browsers into the CORS preflight path for cross-origin JavaScript, but CORS is an access policy, not the source of token unpredictability. Never allow arbitrary origins together with credentials.

Validate Origin against an exact allowlist on unsafe methods. If it is absent in a legitimate case, apply a documented Referer fallback or reject. Normalize scheme, host, and port; suffix tests such as endsWith(example.com) admit attacker domains. Reverse proxies must supply a trusted canonical origin rather than letting untrusted forwarding headers redefine it.

Fetch Metadata adds another independent signal. The W3C Fetch Metadata specification defines request headers including Sec-Fetch-Site, allowing a server to distinguish same-origin, same-site, cross-site, and browser-external requests. Reject cross-site unsafe requests by default, then maintain narrow exceptions for endpoints intentionally designed for cross-site use. Do not make absence automatically trusted because non-browser clients and older clients may omit these headers.

Decide by endpoint class

For same-origin browser JSON APIs, require exact origin plus CSRF header and reject simple content types. For cross-origin first-party SPAs, use a fixed credentialed CORS allowlist, preflighted custom header, origin validation, and a token when feasible. For webhooks, do not use browser cookies; authenticate the sender with a signature or mutually agreed credential. For public OAuth redirects, use the protocol’s state and binding requirements rather than a general API token.

Ory Kratos is an API-first identity and user-management system with self-service flows and support for a custom user interface, as summarized in the Ory Network Kratos introduction. Ory Network is the managed path, while self-hosted Ory Kratos is a separate deployment choice. Apply the threat-driven matrix above to the browser flow and surrounding application.

Prove the boundary

Add negative tests for a cross-site form, text/plain JSON, a missing token, a mismatched origin, a sibling subdomain, Origin: null, and a credentialed CORS request from an unlisted origin. Verify rejection occurs before side effects and that logs capture the rule without recording the token.

The next step is to enumerate every cookie-authenticated mutation and run this matrix against the real gateway and application. Review by 2026-12-05 or after cookie, domain, proxy, or browser-flow changes.

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗