0% found this document useful (0 votes)
12 views

CSRF Token Generation in Spring Boot

This document outlines the process of setting up CSRF (Cross-Site Request Forgery) protection in a Spring Boot application using Spring Security. It details how CSRF tokens are generated, stored, and validated, as well as how to configure the backend and frontend (Angular) to work together for secure API requests. Key points include token flow, security considerations, and example configurations for both Spring Boot and Angular applications.

Uploaded by

newsletter
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

CSRF Token Generation in Spring Boot

This document outlines the process of setting up CSRF (Cross-Site Request Forgery) protection in a Spring Boot application using Spring Security. It details how CSRF tokens are generated, stored, and validated, as well as how to configure the backend and frontend (Angular) to work together for secure API requests. Key points include token flow, security considerations, and example configurations for both Spring Boot and Angular applications.

Uploaded by

newsletter
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 30

CSRF Token Generation in Spring Boot

In Spring Boot (and Spring Security more generally), CSRF (Cross-Site Request
Forgery) tokens are generated and managed automatically by the framework
to protect against CSRF attacks.

When you include Spring Security in your Spring Boot project, CSRF protection
is enabled by default for web applications.

How CSRF Token is Generated

When a user accesses a secured web page:

 Spring Security generates a unique CSRF token for the user session.

 This token is associated with the user's HTTP session


(HttpSessionCsrfTokenRepository by default).

The token is typically generated using something like:

UUID.randomUUID().toString();

CSRF Token Repositories

Spring Security uses a CsrfTokenRepository to store and retrieve CSRF


tokens. Common implementations:

 HttpSessionCsrfTokenRepository: stores token in the HTTP session


(default).

 CookieCsrfTokenRepository: stores the CSRF token in a cookie (often


used for SPAs like Angular or React).

Example:

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
4. CSRF Token in the HTML Page

If you use Thymeleaf or JSP, Spring can automatically include the CSRF token in
forms:

Example (Thymeleaf):

<form method="post" th:action="@{/submit}">


<input type="hidden" th:name="${_csrf.parameterName}" th:value="$
{_csrf.token}" />
...
</form>

For JavaScript-based clients (e.g., Angular, React), the token can be retrieved
from a cookie or an endpoint and sent via header.

CSRF Token Validation

When a POST, PUT, DELETE, or PATCH request is received:

 Spring Security checks for the CSRF token in the request (header or form
field).

 If the token is missing or invalid, a 403 Forbidden is returned.

Customizing or Disabling CSRF

You can disable CSRF protection (not recommended for browser-based clients):

http.csrf(csrf -> csrf.disable());

Or use it selectively for specific endpoints (e.g., enable it for web, disable it for
APIs).

Here's how it works:

Default CSRF Token Generation


1. Token Creation:
o Spring Security generates a random, cryptographically strong
token value
o By default, it uses SecureRandom to create a Base64-encoded string
o The token is typically 32-64 bytes long (exact length depends on
the implementation)
2. Token Storage:

o The token is stored in the HTTP session on the server side


o A copy is also sent to the client, typically in:

 A hidden form field named _csrf (for form submissions)


 Or as a header (for AJAX requests)
3. Token Validation:

o When a state-changing request (POST, PUT, DELETE, etc.) is made:

 Spring Security compares the token from the request with


the one stored in the session
 If they don't match, the request is rejected

Customization Options
You can customize CSRF token behavior in Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// or use custom token repository
// .csrfTokenRepository(new CustomTokenRepository())
);
return http.build();
}
}

Common Token Repository Implementations


1. HttpSessionCsrfTokenRepository (default):
o Stores tokens in the HTTP session
o Expects token in header or parameter named _csrf
2. CookieCsrfTokenRepository:

o Stores token in a cookie named XSRF-TOKEN


o Client reads cookie and sends token in header X-XSRF-TOKEN

Token Generation Process Details


The actual token generation happens in the CsrfToken interface
implementations. The default implementation:

1. Uses SecureRandom for cryptographically strong random numbers


2. Base64 encodes the random bytes
3. Creates a token with:

o A random value (the actual token string)


o A parameter name (default _csrf)
o A header name (default X-CSRF-TOKEN)

Example Flow
1. First GET request:

o Server generates token, stores in session


o Includes token in response (form field or header)
2. Subsequent POST request:

o Client includes token in request


o Server validates token against session-stored token
o If valid, processes request; if not, returns 403

This mechanism ensures that only requests originating from your own site can
perform state-changing operations.
Setting up CSRF protection in a Spring Boot backend with an Angular
frontend:

Spring Boot Configuration (Backend)

1. Basic Security Configuration:

@Configuration

@EnableWebSecurity

public class SecurityConfig {

@Bean

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws


Exception {

http

.csrf(csrf -> csrf

.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)

.authorizeHttpRequests(auth -> auth

.requestMatchers("/api/**").authenticated()

.anyRequest().permitAll()

.formLogin(withDefaults())

.httpBasic(withDefaults());
return http.build();

2. CORS Configuration (for Angular dev server):

@Bean

public WebMvcConfigurer corsConfigurer() {

return new WebMvcConfigurer() {

@Override

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/**")

.allowedOrigins("http://localhost:4200")

.allowedMethods("*")

.allowCredentials(true);

};

Angular Configuration (Frontend)

1. HttpInterceptor for CSRF:

// csrf.interceptor.ts

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from


'@angular/common/http';

import { Observable } from 'rxjs';


export class CsrfInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler):


Observable<HttpEvent<any>> {

// Only add CSRF for non-GET requests to your API

if (!req.method.includes('GET') && req.url.includes('/api/')) {

const csrfToken = this.getCookie('XSRF-TOKEN');

if (csrfToken) {

req = req.clone({

withCredentials: true,

setHeaders: {

'X-XSRF-TOKEN': csrfToken

});

return next.handle(req);

private getCookie(name: string): string | null {

const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]


+)'));

return match ? match[2] : null;

}
}

2. Register the Interceptor:

// app.module.ts

import { HTTP_INTERCEPTORS } from '@angular/core';

import { CsrfInterceptor } from './csrf.interceptor';

@NgModule({

providers: [

{ provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }

})

export class AppModule {}

3. Service Example:

// data.service.ts

import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })

export class DataService {

constructor(private http: HttpClient) {}

postData(data: any) {

return this.http.post('/api/data', data, {

withCredentials: true
});

Key Points:

1. Token Flow:

o Spring sends XSRF-TOKEN cookie (automatically


via CookieCsrfTokenRepository)

o Angular reads cookie and sends back as X-XSRF-TOKEN header

2. Important Headers:

Cookie: XSRF-TOKEN=abc123

X-XSRF-TOKEN: abc123

3. Security Considerations:

o Ensure withCredentials: true in Angular requests

o Configure CORS properly on backend

o Keep HttpOnly=false for the CSRF cookie (so Angular can read it)

4. Testing Endpoint (Spring Boot):

@RestController

public class TestController {

@GetMapping("/api/csrf")

public String getCsrf() {

return "CSRF protected endpoint";

}
@PostMapping("/api/test")

public String testPost() {

return "POST successful";

Verification Steps:

1. Check browser cookies after first GET request - should see XSRF-TOKEN

2. Inspect POST requests in DevTools - should include both:

o Cookie header with XSRF-TOKEN

o X-XSRF-TOKEN header

3. Try removing the token header - should get 403 Forbidden


Spring Boot Backend Configuration

1. Spring Security Setup

Configure Spring Security to use CookieCsrfTokenRepository with HttpOnly set


to false, allowing JavaScript to access the CSRF token stored in a cookie:

java

CopyEdit

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import
org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.web.SecurityFilterChain;

import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

@Configuration

public class SecurityConfig {

@Bean

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http

.csrf(csrf -> csrf

.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)

)
.authorizeHttpRequests(auth -> auth

.requestMatchers("/api/**").permitAll()

.anyRequest().authenticated()

.httpBasic();

return http.build();

This configuration ensures that:

 CSRF tokens are stored in cookies accessible to JavaScript.

 API endpoints under /api/** are publicly accessible.

 All other endpoints require authentication.

2. Serving the Angular Application

If you're serving the Angular frontend through Spring Boot, ensure that your
controller maps to the appropriate route:

java

CopyEdit

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.GetMapping;

@Controller

public class FrontendController {


@GetMapping("/")

public String index() {

return "index.html";

🌐 Angular Frontend Configuration

1. HTTP Interceptor for CSRF Token

Create an HTTP interceptor to read the CSRF token from the cookie and include
it in the headers of HTTP requests:

typescript

CopyEdit

// csrf.interceptor.ts

import { Injectable } from '@angular/core';

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from


'@angular/common/http';

import { Observable } from 'rxjs';

@Injectable()

export class CsrfInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler):


Observable<HttpEvent<any>> {

const csrfToken = this.getCookie('XSRF-TOKEN');

if (csrfToken) {
req = req.clone({

setHeaders: {

'X-XSRF-TOKEN': csrfToken

});

return next.handle(req);

private getCookie(name: string): string | null {

const cookies = document.cookie.split(';');

for (const cookie of cookies) {

const [k, v] = cookie.trim().split('=');

if (k === name) return decodeURIComponent(v);

return null;

2. Registering the Interceptor

In your app.module.ts, register the interceptor:

typescript

CopyEdit

import { HTTP_INTERCEPTORS } from '@angular/common/http';


import { CsrfInterceptor } from './csrf.interceptor';

@NgModule({

...

providers: [

{ provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }

})

export class AppModule { }

3. Making HTTP Requests

With the interceptor in place, you can make HTTP requests as usual, and the
CSRF token will be automatically included:

typescript

CopyEdit

this.http.post('/api/save-data', { name: 'Test' }).subscribe(response => {

console.log('Saved:', response);

});

🔄 Workflow Summary

1. Initial Request: When the Angular application loads, it makes an initial


request to the Spring Boot backend.

2. CSRF Token Generation: Spring Security generates a CSRF token and


stores it in a cookie named XSRF-TOKEN.
3. Token Retrieval: The Angular interceptor reads the XSRF-TOKEN cookie
and includes its value in the X-XSRF-TOKEN header of subsequent HTTP
requests.

4. Token Validation: Spring Security validates the CSRF token from the
header against the one stored in the cookie. If they match, the request is
processed; otherwise, it's rejected.

📚 Reference Implementation

For a practical example, you can refer to the following GitHub repository:

 Spring Security and Angular CSRF Example: This project


demonstrates login, logout, CSRF protection, and authentication guards
using Spring Security 6 and Angular. GitHubGitHub+1YouTube+1
Here's a comprehensive guide to setting up CSRF protection in a Spring Boot
backend with an Angular frontend:

Spring Boot Configuration (Backend)

1. Basic Security Configuration:

java

Copy

Download

@Configuration

@EnableWebSecurity

public class SecurityConfig {

@Bean

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws


Exception {

http

.csrf(csrf -> csrf

.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)

.authorizeHttpRequests(auth -> auth

.requestMatchers("/api/**").authenticated()

.anyRequest().permitAll()

)
.formLogin(withDefaults())

.httpBasic(withDefaults());

return http.build();

2. CORS Configuration (for Angular dev server):

java

Copy

Download

@Bean

public WebMvcConfigurer corsConfigurer() {

return new WebMvcConfigurer() {

@Override

public void addCorsMappings(CorsRegistry registry) {

registry.addMapping("/**")

.allowedOrigins("http://localhost:4200")

.allowedMethods("*")

.allowCredentials(true);

};

Angular Configuration (Frontend)


1. HttpInterceptor for CSRF:

typescript

Copy

Download

// csrf.interceptor.ts

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from


'@angular/common/http';

import { Observable } from 'rxjs';

export class CsrfInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler):


Observable<HttpEvent<any>> {

// Only add CSRF for non-GET requests to your API

if (!req.method.includes('GET') && req.url.includes('/api/')) {

const csrfToken = this.getCookie('XSRF-TOKEN');

if (csrfToken) {

req = req.clone({

withCredentials: true,

setHeaders: {

'X-XSRF-TOKEN': csrfToken

});

}
}

return next.handle(req);

private getCookie(name: string): string | null {

const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]


+)'));

return match ? match[2] : null;

2. Register the Interceptor:

typescript

Copy

Download

// app.module.ts

import { HTTP_INTERCEPTORS } from '@angular/core';

import { CsrfInterceptor } from './csrf.interceptor';

@NgModule({

providers: [

{ provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }

})
export class AppModule {}

3. Service Example:

typescript

Copy

Download

// data.service.ts

import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })

export class DataService {

constructor(private http: HttpClient) {}

postData(data: any) {

return this.http.post('/api/data', data, {

withCredentials: true

});

Key Points:

1. Token Flow:

o Spring sends XSRF-TOKEN cookie (automatically


via CookieCsrfTokenRepository)

o Angular reads cookie and sends back as X-XSRF-TOKEN header


2. Important Headers:

Copy

Download

Cookie: XSRF-TOKEN=abc123

X-XSRF-TOKEN: abc123

3. Security Considerations:

o Ensure withCredentials: true in Angular requests

o Configure CORS properly on backend

o Keep HttpOnly=false for the CSRF cookie (so Angular can read it)

4. Testing Endpoint (Spring Boot):

java

Copy

Download

@RestController

public class TestController {

@GetMapping("/api/csrf")

public String getCsrf() {

return "CSRF protected endpoint";

@PostMapping("/api/test")

public String testPost() {


return "POST successful";

Verification Steps:

1. Check browser cookies after first GET request - should see XSRF-TOKEN

2. Inspect POST requests in DevTools - should include both:

o Cookie header with XSRF-TOKEN

o X-XSRF-TOKEN header

3. Try removing the token header - should get 403 Forbidden

Would you like me to provide:

1. A complete GitHub repository with this setup?

2. Additional security configurations (like JWT + CSRF combo)?

3. Testing strategies for CSRF protection?

A complete GitHub repository with this setup

I'll outline a complete GitHub repository structure for a Spring Boot + Angular
application with CSRF protection. You can find a ready-to-use implementation
here:

GitHub Repository:
🔗 https://github.com/example/spring-boot-angular-csrf-demo (Note: This is a
placeholder link - see below for actual implementation steps)

Repository Structure

Copy

Download
spring-boot-angular-csrf-demo/

├── backend/ # Spring Boot application

│ ├── src/

│ │ ├── main/

│ │ │ ├── java/com/example/demo/

│ │ │ │ ├── config/

│ │ │ │ │ └── SecurityConfig.java

│ │ │ │ ├── controller/

│ │ │ │ │ └── ApiController.java

│ │ │ │ └── DemoApplication.java

│ │ │ └── resources/

│ │ │ └── application.properties

│ │ └── test/

│ └── pom.xml

├── frontend/ # Angular application

│ ├── src/

│ │ ├── app/

│ │ │ ├── services/

│ │ │ │ └── api.service.ts

│ │ │ ├── interceptors/

│ │ │ │ └── csrf.interceptor.ts

│ │ │ └── app.module.ts

│ │ ├── assets/
│ │ ├── environments/

│ │ └── index.html

│ ├── angular.json

│ └── package.json

└── README.md

How to Set This Up Locally

1. Backend (Spring Boot):

bash

Copy

Download

# Generate new Spring Boot project

spring init --dependencies=web,security backend

2. Frontend (Angular):

bash

Copy

Download

# Create new Angular app

ng new frontend

cd frontend

ng generate interceptor interceptors/csrf

Key Files Implementation


1. Spring Boot Security
Config (backend/src/main/java/.../SecurityConfig.java):

java

Copy

Download

@Configuration

@EnableWebSecurity

public class SecurityConfig {

@Bean

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws


Exception {

http

.csrf(csrf -> csrf

.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)

.ignoringRequestMatchers("/api/public/**")

.authorizeHttpRequests(auth -> auth

.requestMatchers("/api/public/**").permitAll()

.anyRequest().authenticated()

.cors(cors -> cors.configurationSource(corsConfigurationSource()));


return http.build();

@Bean

CorsConfigurationSource corsConfigurationSource() {

CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));

configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));

configuration.setAllowCredentials(true);

configuration.addExposedHeader("X-XSRF-TOKEN");

UrlBasedCorsConfigurationSource source = new


UrlBasedCorsConfigurationSource();

source.registerCorsConfiguration("/**", configuration);

return source;

2. Angular CSRF
Interceptor (frontend/src/app/interceptors/csrf.interceptor.ts):

typescript

Copy

Download

import { Injectable } from '@angular/core';

import {
HttpRequest,

HttpHandler,

HttpEvent,

HttpInterceptor

} from '@angular/common/http';

import { Observable } from 'rxjs';

@Injectable()

export class CsrfInterceptor implements HttpInterceptor {

intercept(request: HttpRequest<unknown>, next: HttpHandler):


Observable<HttpEvent<unknown>> {

const token = this.getCookie('XSRF-TOKEN');

const isApiRequest = request.url.startsWith('/api');

const isMutatingRequest = ['POST', 'PUT', 'DELETE',


'PATCH'].includes(request.method);

if (isApiRequest && isMutatingRequest && token) {

request = request.clone({

withCredentials: true,

setHeaders: {

'X-XSRF-TOKEN': token

});
}

return next.handle(request);

private getCookie(name: string): string | null {

const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]


+)'));

return match ? decodeURIComponent(match[2]) : null;

Testing the Implementation

1. Start both applications:

bash

Copy

Download

# In backend directory

./mvnw spring-boot:run

# In frontend directory

ng serve

2. Verify CSRF protection:


 Open DevTools and check:

o Cookies tab for XSRF-TOKEN

o Network tab for X-XSRF-TOKEN header in POST requests

You might also like