Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
manjosh1990 opened this issue Aug 11, 2022 · 7 comments
Assignees
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug

Comments

@manjosh1990
Copy link

manjosh1990 commented Aug 11, 2022

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 manjosh1990 added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Aug 11, 2022
@manjosh1990
Copy link
Author

Here is the old bug opened for the same issue
#11620

@sjohnr sjohnr self-assigned this Aug 11, 2022
@sjohnr sjohnr added the in: web An issue in web modules (web, webmvc) label Aug 11, 2022
sjohnr added a commit to sjohnr/spring-security that referenced this issue Aug 18, 2022
@sjohnr
Copy link
Member

sjohnr commented Aug 18, 2022

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().

private Mono<Boolean> containsValidCsrfToken(ServerWebExchange exchange, CsrfToken expected) {
return exchange.getFormData().flatMap((data) -> Mono.justOrEmpty(data.getFirst(expected.getParameterName())))
.switchIfEmpty(Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(expected.getHeaderName())))
.switchIfEmpty(tokenFromMultipartData(exchange, expected))
.map((actual) -> equalsConstantTime(actual, expected.getToken()));
}

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 sjohnr removed the status: waiting-for-triage An issue we've not yet triaged label Aug 18, 2022
@sjohnr sjohnr added this to the General Backlog milestone Aug 18, 2022
@manjosh1990
Copy link
Author

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

@manjosh1990
Copy link
Author

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

@manisha-shetty
Copy link

Any update on a solution to this issue?

@maradanasai
Copy link

Any update on this?

@sjohnr
Copy link
Member

sjohnr commented May 12, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web An issue in web modules (web, webmvc) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants