CSRF

What Is CSRF?

Cross-Site Request Forgery: you are logged into ShopEase in one tab. In another tab you open an evil page. That page silently submits a form to ShopEase. The browser automatically attaches your session cookie, so ShopEase thinks you sent it.

<!-- on evil-site.com -->
<form action="https://shopease.in/account/email" method="POST">
  <input type="hidden" name="email" value="[email protected]">
</form>
<script>document.forms[0].submit()</script>

The Fix: A Secret Token

Example

Spring Security puts a random CSRF token in your session and in every form it renders. A state-changing request (POST, PUT, PATCH, DELETE) must send it back. The evil site cannot read it, so its forged request fails:

import java.util.*;

public class Main {
    // session id -> csrf token (Spring stores it in the HttpSession)
    static final Map<String, String> CSRF = new HashMap<>();

    static String handle(String method, String sessionId, String sentToken) {
        boolean safe = method.equals("GET") || method.equals("HEAD") || method.equals("OPTIONS");
        if (safe) return method + " -> 200 (no token needed)";
        String expected = CSRF.get(sessionId);
        if (expected == null || !expected.equals(sentToken)) return method + " -> 403 Invalid CSRF token";
        return method + " -> 200 email changed";
    }

    public static void main(String[] args) {
        CSRF.put("S1001", "a8f3-77c2-91de");   // generated at login, embedded in the real form

        System.out.println("Real ShopEase form:  " + handle("POST", "S1001", "a8f3-77c2-91de"));
        System.out.println("Evil site (no token): " + handle("POST", "S1001", null));
        System.out.println("Evil site (guess):    " + handle("POST", "S1001", "0000-0000-0000"));
        System.out.println("Page view:            " + handle("GET", "S1001", null));
    }
}

Output

Real ShopEase form:  POST -> 200 email changed
Evil site (no token): POST -> 403 Invalid CSRF token
Evil site (guess):    POST -> 403 Invalid CSRF token
Page view:            GET -> 200 (no token needed)

CSRF in Spring Security

  • CSRF protection is on by default for POST, PUT, PATCH and DELETE.
  • Thymeleaf forms get the hidden _csrf field automatically; the generated login and logout pages include it too.
  • A POST with curl and no token returns 403 Forbidden, a very common beginner surprise.
  • It is safe to disable CSRF only for APIs that do not use cookies for authentication (HTTP Basic or Bearer tokens sent by code).
// Stateless API chain: no session cookie, so CSRF cannot happen
http.csrf(csrf -> csrf.disable());

// Or keep it for the web pages and ignore only the API paths
http.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**"));

Note: SPA + cookie setups (React with a session cookie) need CookieCsrfTokenRepository; that is covered in the medium level, CORS and CSRF in Practice.