Interview Prep Zoneby CuriouserLabs

Question 4 of 5 · senior level

CSRF and CORS: what does each actually protect against, and when do you disable CSRF?

🎤 Say this first

Different threats, constantly confused. CSRF: a malicious site makes the victim's browser send a state-changing request to your app with the victim's cookies automatically attached — the defense (synchronizer tokens, SameSite cookies) proves the request came from your own UI. Only relevant when authentication rides in something the browser attaches automatically (cookies/session). CORS: a browser-enforced read protection controlling which origins' scripts may call your API and see responses — configured server-side (allowed origins/methods/headers), preflighted for non-simple requests. Disable CSRF for pure token-based APIs (Authorization header isn't auto-attached — the attack doesn't exist); keep it wherever cookies/sessions authenticate.

The full picture

  • The decision rule: 'could a cross-site request arrive pre-authenticated?' Cookies/Basic → yes → CSRF protection on. Bearer-token-only → no → csrf.disable() is correct, not lazy. Cookie-based SPA → CSRF on (cookie-to-header pattern) plus SameSite=Lax/Strict as modern layered defense.
  • CORS is not access control: it protects browser users, not your API — curl ignores it entirely. Auth still does the real protection; CORS just stops malicious-origin JS from riding a user's browser session to read responses.
  • CORS in Spring: configure in the security chain (http.cors(…) + a CorsConfigurationSource) so preflights (OPTIONS) pass before auth filters reject them — the 'CORS works locally, 401s on preflight in prod' classic.
  • Never allowedOrigins("*") with allowCredentials(true) — the spec forbids it and Spring throws; wildcard origins with credentials would be cookie-theft-as-a-service. Use explicit origins or patterns.
Cookie-authenticated SPA: both, correctly
http
  .cors(c -> c.configurationSource(req -> {
      var cfg = new CorsConfiguration();
      cfg.setAllowedOrigins(List.of("https://app.acme.com"));   // explicit
      cfg.setAllowedMethods(List.of("GET","POST","PUT","DELETE"));
      cfg.setAllowedHeaders(List.of("Content-Type","X-XSRF-TOKEN"));
      cfg.setAllowCredentials(true);
      return cfg;
  }))
  .csrf(csrf -> csrf
      .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
      .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()));
// SPA reads XSRF-TOKEN cookie -> sends X-XSRF-TOKEN header; attacker's site
// can trigger the request but cannot READ the cookie to forge the header.

🔄 Likely follow-up questions

  • How does SameSite=Lax change the CSRF calculus, and why keep tokens anyway?
  • Why must CORS be handled before authentication in the filter chain?
  • What's the BFF pattern and how does it simplify this whole picture?