Spring Boot (2.1)
By default, the CSRF protection is enabled in the WebSecurityConfigurerAdapter default constructor
We could disable it in this way in configure(HttpSecurity http)
:
http.csrf().disable() |
And we could also override the default configuration for CSRF.
For example, the token in CSRF has different options to be associated to a request. It may be stored in HttpSession, use cookies and so for…
Spring introduces the CsrfTokenRepository as interface to make the token-request association.
If we want to use the cookie token strategy we could do that :
http.csrf() .csrfTokenRepository(new CookieCsrfTokenRepository()) |
And that’s all for the server side.
Client side cookie-token strategy
The overall idea is simple :
When the clients perform a request from another domain, when the HTTP session is created, in the response is added a cookie that represents the CRSF token.
Then the client uses that token both in cookies (classic cookie retransmssion) and in request headers for any request subject to be protected against CRSF.
The server (here Spring Boot) checks that the two sent tokens (cookie and header) have the same value. If these match, the request is processed. Otherwise, a 403 response is returned.
With vanilla JS or almost raw JS, that is very simple. With more sophisticated JS frameworks, things may be much more complicated.
The cookie name may differ according to your configuration and the backend technology used. With Spring Boot, the cookie name is XSRF-TOKEN.
It depends of the technologies used.But whatever, in any case, from the client side you should be able to adapt your code to retrieve the token with any name and to re-transmit it in the headers both as cookie and as « simple » header for any request sensitive to CRSF.
Here is an real example executed in local with curl.
We performe two request : the first to be authenticated and get the XSRF token and a second where we POST something (here a quizz).
First step : login and store cookies
curl -v -c storedcookies -H "content-type: application/x-www-form-urlencoded" \ -H "authorization: Basic YTph" \ http://localhost:8282/api/users/processLoginXXX |
Flow :
-------------------------------------------------------- * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8282 (#0) > GET /api/users/processLoginXXX HTTP/1.1 > Host: localhost:8282 > User-Agent: curl/7.58.0 > Accept: */* > content-type: application/x-www-form-urlencoded > authorization: Basic YTph > < HTTP/1.1 200 * cookie size: name/val 10 + 32 bytes * cookie size: name/val 4 + 1 bytes * cookie size: name/val 8 + 0 bytes * Added cookie JSESSIONID="DAA40C67D20A6CABFED5020E03E189B3" for domain localhost, path /, expire 0 < Set-Cookie: JSESSIONID=DAA40C67D20A6CABFED5020E03E189B3; Path=/; HttpOnly * cookie size: name/val 10 + 36 bytes * cookie size: name/val 4 + 1 bytes * cookie size: name/val 8 + 0 bytes * Added cookie XSRF-TOKEN="07bbb253-fcb9-4dd2-924a-874ab9e9bf53" for domain localhost, path /, expire 0 < Set-Cookie: XSRF-TOKEN=07bbb253-fcb9-4dd2-924a-874ab9e9bf53; Path=/; HttpOnly < X-Content-Type-Options: nosniff < X-XSS-Protection: 1; mode=block < Cache-Control: no-cache, no-store, max-age=0, must-revalidate < Pragma: no-cache < Expires: 0 < X-Frame-Options: DENY < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Fri, 08 Nov 2019 16:31:01 GMT < * Connection #0 to host localhost left intact -------------------------------------------------------- |
Second step : use cookies and post quizz (change the token with which one received)
curl -v -b storedcookies \ -H "content-type: application/json" \ -H "X-XSRF-TOKEN: 07bbb253-fcb9-4dd2-924a-874ab9e9bf53" \ -d @data_curl http://localhost:8282/api/quizzs (works also if we add -H "authorization: Basic YTph") |
Flow :
-------------------------------------------------------- * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8282 (#0) > POST /api/quizzs HTTP/1.1 > Host: localhost:8282 > User-Agent: curl/7.58.0 > Accept: */* > Cookie: JSESSIONID=DAA40C67D20A6CABFED5020E03E189B3; XSRF-TOKEN=07bbb253-fcb9-4dd2-924a-874ab9e9bf53 > content-type: application/json > X-XSRF-TOKEN: 07bbb253-fcb9-4dd2-924a-874ab9e9bf53 > Content-Length: 792 > * upload completely sent off: 792 out of 792 bytes < HTTP/1.1 201 < Location: /api/quizzs/7 < X-Content-Type-Options: nosniff < X-XSS-Protection: 1; mode=block < Cache-Control: no-cache, no-store, max-age=0, must-revalidate < Pragma: no-cache < Expires: 0 < X-Frame-Options: DENY < Content-Length: 0 < Date: Fri, 08 Nov 2019 16:32:21 GMT < * Connection #0 to host localhost left intact -------------------------------------------------------- |