Skip to content

Re-enable CSRF protection for the session-backed BFF #7

Description

@simonforsberg

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant config with line numbers
FILE="bff/src/main/java/org/example/bff/config/BffConfig.java"
if [ -f "$FILE" ]; then
  echo "== $FILE =="
  nl -ba "$FILE" | sed -n '1,140p'
else
  echo "Missing $FILE"
fi

# Search for CSRF disable and any other security config affecting CSRF/session
echo "== rg: csrf.disable =="
rg -n "csrf\s*\(\s*[^)]*disable\s*\(\s*\)\s*\)|csrf\s*\.disable\s*\(|csrf\s*->\s*.*disable" -S .

echo "== rg: authorizeHttpRequests/auth anyRequest =="
rg -n "authorizeHttpRequests|anyRequest\(\)\.authenticated\(\)" -S bff/src/main/java || true

echo "== rg: oauth2Login or sessionManagement =="
rg -n "oauth2Login|oauth2Client|sessionManagement|SessionCreationPolicy|JSESSIONID" -S bff/src/main/java || true

echo "== Files with security filter chain =="
rg -n "SecurityFilterChain|HttpSecurity" -S bff/src/main/java/org/example/bff/config || true

Repository: simonforsberg/relay

Length of output: 186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="bff/src/main/java/org/example/bff/config/BffConfig.java"

echo "== repo files (bff config path existence) =="
ls -la bff/src/main/java/org/example/bff/config/ 2>/dev/null || true
if [ -f "$FILE" ]; then
  echo "Found: $FILE"
else
  echo "Missing: $FILE"
  # Fallback: locate similarly named files
  echo "== locating BffConfig.java =="
  fd -a "BffConfig.java" . || true
fi

if [ -f "$FILE" ]; then
  echo "== showing lines 1-120 with numbers (cat -n) =="
  cat -n "$FILE" | sed -n '1,140p'

  echo "== rg: csrf.disable patterns =="
  rg -n "csrf\s*\(.*\)|csrf\.disable\(\)|csrf\s*->.*disable" -S "$FILE" || true

  echo "== rg: oauth2Login/session auth hints =="
  rg -n "oauth2Login|oauth2Client|sessionManagement|JSESSIONID|CookieCsrfTokenRepository|csrfTokenRepository" -S "$FILE" || true

  echo "== locate SecurityFilterChain(s) in config package =="
  rg -n "SecurityFilterChain|HttpSecurity" -S bff/src/main/java/org/example/bff/config || true
fi

Repository: simonforsberg/relay

Length of output: 4621


🌐 Web query:

Spring Security OAuth2Login stores authentication in HttpSession by default

💡 Result:

Yes—by default, Spring Security OAuth2 login for Servlet (oauth2Login) saves the authenticated user’s SecurityContext in the HttpSession, which effectively stores the Authentication in HttpSession. This is because Spring Security uses a SecurityContextRepository to persist the SecurityContext between requests, and the documented default mechanism is HttpSessionSecurityContextRepository, which “associates the SecurityContext to the HttpSession” [1]. During OAuth2 login, after the Authorization Response is successfully processed and an OAuth2AuthenticationToken is created, it is “ultimately stored in the SecurityContextRepository” [2]. With the default SecurityContextRepository setup, that repository persists to HttpSession via HttpSessionSecurityContextRepository [1]. Note: your query mentions “Spring Security OAuth2Login” generically; the sources above are for Servlet-based Spring Security. For Reactive (WebFlux), the equivalent defaults use a WebSession-backed repository instead of HttpSession [3].

Citations:


🌐 Web query:

CookieCsrfTokenRepository default cookie/header names XSRF-TOKEN X-XSRF-TOKEN X-CSRF-TOKEN

💡 Result:

In Spring Security, the CookieCsrfTokenRepository (and its reactive equivalent, CookieServerCsrfTokenRepository) uses the following default names, which are designed to follow AngularJS conventions [1][2][3][4]: - Cookie Name: XSRF-TOKEN [1][2][4] - Header Name: X-XSRF-TOKEN [1][2][4] The repository persists the CSRF token in a cookie named "XSRF-TOKEN" and reads the token from the "X-XSRF-TOKEN" HTTP header during request validation [1][2]. By default, the cookie created by this repository has the HttpOnly flag set to true to enhance security by preventing client-side JavaScript access [2][4][5]. However, because many single-page application (SPA) frameworks (like AngularJS or other custom JavaScript-based clients) need to read this cookie value to set it in the required HTTP request header, Spring Security provides the.withHttpOnlyFalse factory method [1][6][3][7]. Using this method allows JavaScript running on the client to read the cookie so it can be manually included in the X-XSRF-TOKEN header for subsequent requests [6][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# find other security configs in repo
rg -n "SecurityFilterChain|HttpSecurity|csrf\(|csrf\.|oauth2Login|logout\(|LogoutConfigurer" -S bff/src/main/java || true

# specifically look for logout request matcher overrides
rg -n "logoutRequestMatcher|LogoutRequestMatcher|LogoutConfigurer|postOnly|LogoutConfigurer" -S . || true

Repository: simonforsberg/relay

Length of output: 772


Re-enable CSRF protection for the session-backed BFF (bff/src/main/java/org/example/bff/config/BffConfig.java:38-43)

This disables CSRF (.csrf(csrf -> csrf.disable())) while oauth2Login(Customizer.withDefaults()) uses the default HttpSessionSecurityContextRepository, so authenticated requests to the BFF (including logout and proxied state-changing POST/DELETE routes) are no longer protected against cross-site request forgery.

🛡️ Proposed fix
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
...
-                .csrf(csrf -> csrf.disable())
+                .csrf(csrf -> csrf
+                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))

Frontend should read the XSRF-TOKEN cookie and send it back as the X-XSRF-TOKEN header on POST/DELETE requests.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated())
                .csrf(csrf -> csrf
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
                .oauth2Login(Customizer.withDefaults())
                .oauth2Client(Customizer.withDefaults())
                .logout(logout -> logout
                        .logoutSuccessHandler(logoutSuccessHandler)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bff/src/main/java/org/example/bff/config/BffConfig.java` around lines 39 -
45, The CSRF protection was disabled in BffConfig which leaves session-backed
oauth2Login and state-changing routes vulnerable; re-enable CSRF by removing the
csrf().disable() call and configure a CookieCsrfTokenRepository (e.g., in the
csrf(...) lambda call, set
csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) so the
server emits an XSRF-TOKEN cookie and accepts the X-XSRF-TOKEN header from the
frontend; keep the existing oauth2Login(...) and logout(logout ->
logout.logoutSuccessHandler(logoutSuccessHandler)) configuration unchanged so
logout and proxied POST/DELETE routes are protected while the frontend reads the
XSRF-TOKEN cookie and sends it as X-XSRF-TOKEN.

Originally posted by @coderabbitai[bot] in #6 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions