spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Enabling CSRF on spring cloud gateway removes formData from POST requests and returns 400 Bad request error

Open manjosh1990 opened this issue 3 years ago • 4 comments

Describe the bug I have enabled CSRF on my spring cloud API gateway server. I have angular as my GUI framework which calls the rest services through the API gateway. There are other underlying rest services after the API gateway. The spring cloud gateway acts a gatekeeper to all the requests to these rest services. These rest services are developed using spring web MVC. I have used a custom filter to add the CSRF token to the response headers. When the POST call is made I see that the formData is lost. So I always get 400 Bad request errors. I disabled CSRF and the request goes through fine without any issues.

Looks like some filter is consuming the body and not forwarding it.

To Reproduce Here is my sample project https://github.com/manjosh1990/webgateway-issues whenever the POST request is made, I get 400 bad request responses.

Expected behavior The API should not consume the formData and pass it on to the underlying microservices.

Sample

Here is the link to the sample project: https://github.com/manjosh1990/webgateway-issues I had raised a question on stackoverflow https://stackoverflow.com/questions/73117195/csrf-on-spring-cloud-gateway-removing-formdata-from-post-requests-400-bad-reques?noredirect=1#comment129469661_73117195

manjosh1990 avatar Aug 11 '22 09:08 manjosh1990

Here is the old bug opened for the same issue https://github.com/spring-projects/spring-security/issues/11620

manjosh1990 avatar Aug 11 '22 09:08 manjosh1990

Thanks for finding this, @manjosh1990! Sorry to give you a bit of the runaround with a stackoverflow question to get here.

I have dug into this issue deeper, and believe the issue is on the Spring Security side. The CsrfWebFilter seems to assume that once the Content-Type header is determined to be application/x-www-form-urlencoded, consumers of the request body will always use the cached form data available in exchange.getFormData().

https://github.com/spring-projects/spring-security/blob/63d2f19e2a40321be631cd08d5687271e55eef22/web/src/main/java/org/springframework/security/web/server/csrf/CsrfWebFilter.java#L140-L145

Since Spring Cloud Gateway works at a higher level and treats all requests the same way, it doesn't use this method to access the request body and instead accesses exchange.getRequest().getBody(). See this line.

Besides causing odd behavior (the request appears to hang for quite some time), it ultimately causes the request to be sent downstream without a body since the request body was consumed by Spring Security.

I see a couple of possible solutions:

  1. Cache the request body prior to retrieving form data.
  2. Add a configuration option allowing an application to opt-out of checking form data for a csrf token.
  3. Change the order in which a csrf token is checked to prefer a header when available.

Options two and three aren't mutually exclusive, so we could potentially change the order as well, which would allow applications that want to use form data with Spring Security + Spring Cloud Gateway to provide the csrf token in a header and side-step the issue.

I've added a temporary workaround to the stackoverflow question, in the event you're completely stuck on this issue. I'm adding this to the general backlog at this time, as we have quite a few remaining 5.8 items. If you'd be interested in picking this up, I've pushed a branch with a quick stab at option 2.

sjohnr avatar Aug 18 '22 20:08 sjohnr

We are facing this issue in a production environment. We'll see if we can ignore CSRF validation forAPPLICATION_FORM_URLENCODED requests. Thanks

manjosh1990 avatar Aug 22 '22 06:08 manjosh1990

I tried to disable CSRF for APPLICATION_FORM_URLENCODED request, but now I get "Invalid CSRF token" even for GET requests. Also, why is it required to have a filter to add the CSRF token into the request stream. Spring security should be adding it by default unless it's disabled right? The documentation does not provide any information on this filter. I have pushed the changes to the repository, can you have a look

manjosh1990 avatar Sep 19 '22 03:09 manjosh1990

Any update on a solution to this issue?

manisha-shetty avatar Mar 24 '23 19:03 manisha-shetty

Any update on this?

maradanasai avatar May 11 '23 09:05 maradanasai

No update @maradanasai. If anyone is interested in taking this up, see b6ec357 for one possible starting point.

sjohnr avatar May 12 '23 18:05 sjohnr