Spring Security auto CSRF token creation is not working on Firefox - spring

When I upload a file through my app that uses Spring framework and spring security, it sends a CSRF token in the request through Spring to prevent CSRF. This token does not seem to be generated as it's left out of the request URL in Firefox. Again, this is happening in Firefox only. Does anyone have any experience with this and/or have any insight as to why this is happening? I've tried accepting cookies from this website in the browser and doing a few hacks in my code to allow it, but none of it seems to work. Thank you.

In case anyone is wondering, it turns out that my async call to retrieve a new CSRF token wasn't firing on time and the form would post without it, hence causing this error message from Spring. Check if you're posting the form prematurely if you're getting this error.

Related

CSRF token in rest api

Using CSRF token in rest API is helpful or not ? as far as I know we don't have a session so we should send the token to client for next request or for submitting the form.
Is it helpful to use it again in ajax(xhr) calls. are there any alternatives ?
I've been reading the spring documents for this, and it also has some examples. but I was curious that is it really helpful or not ?
By the way My server is spring 2.2 and my client is Angular 9.
Thanks
CSRF tokens are essential for preventing XSS attacks, for instance you are logged into your bank, in one tab, and visiting my malicious site that will send a hidden form to your bank stealing your credicard number.
If you want to build a more secure site, every request that manipulates the state in the backend (POST, PUT, DELETE etc) should have a CSRF token included, to ensure that the request came from forms on your site and only your site.
You can read more about CSRF tokens on Owasps webpage.

Why should we call sanctum/csrf-cookie on Laravel Sanctum

I was reading document, and one question occurred. Why would we need to call this endpoint /sanctum/csrf-cookie to get CSRF protection when login?
I understand what CSRF is, and per my understanding, the practice that Laravel uses to prevent CSRF is to set a cookie xsrf-token on browser and then Angular or some framework would automatically attach the cookie to header as x-xsrf-token, and it's also called server side double submit as one of the practices to prevent CSRF
However, I just don't get why on Laravel Sanctum we have to manually call /sanctum/csrf-cookie before login. With Web guard, this protection is automatic after login without any manual work before login.
My question is what is the benefit or logic for calling /sanctum/csrf-cookie before login rather than automatically sending x-csrf-cookie to browser via response after login?
Anyone could help to further explain will be so much appreciated.

CSRF token not sent from Angular to Spring

We are building a web application using Angular and Spring Boot. As one of our security measures, we use CSRF tokens. The issue is that, on our local machines, the token validation works, but on our staging server, the tokens aren't sent by the frontend. The problem has suddenly occurred; we didn't have this problem for the first few months of using the tokens. After a certain build, they started failing. Then after a while, they were working again, but now have ceased working once more.
So, it seems like there is an environment issue that we fail to see.
What we can see
Spring is creating the CSRF token and sending it on preflight (OPTIONS) requests. It encapsulates the token in the Set-Cookie header, which Angular reads using the CookieXSRFStrategy. Again, locally this works fine, but on our staging server this fails. The issue is that Angular doesn't set the cookie after receiving the token. We checked this by inspecting the cookies in the Google Dev Console.
The specific error given by Spring is:
Invalid CSRF Token was found on the request parameter '_csrf' or
header 'X-CSRF-TOKEN'
The issue this causes
We can't login, since Spring doesn't receive a CSRF token. Therefore we can't do anything with the application unless we turn off CSRF protection completely.
We have tried
Specifically whitelisting cookies for the application in Chrome.
Clearing cookies and cache.
Trying in different browsers, on different machines. This happens in Chrome, IE11 and Firefox. We haven't tried any other browsers.
Making sure the origin is allowed through CORS, which it is. We do this with the Access-Control-Allow-Origin header.
Exposing more headers, like Set-Cookie using the Access-Control-Expose-Headers header. This doesn't change anything.
Allowing more headers, using Access-Control-Allow-Headers. This doesn't change anything either.
Simulating the login process using cURL (with the Postman application). This works. So it's an issue of Angular not being able to process the token properly.
Reading the headers manually with JavaScript/TypeScript. This didn't seem to work since the header wasn't exposed.
After struggling with the issue, we decided to integrate Docker, in an effort to mirror our staging and local environments. This didn't resolve the issue.
Reverting back to a previous build didn't work either.
Important notes
Both locally and on the staging environment we use an SSL certificate. Locally, we use a self-signed certificate.
All of the application runs on a Wildfly server. Both the Angular and Spring code is built and distributed using WAR files, which Wildfly deploys.
We are using a corporate proxy. The staging server is hosted in-house, and is only accessible when you're on the corporate network.
Versions
Angular: 4.2.2
Spring Boot: 1.4.1
Wildfly: 10.1.0.Final
Our current workaround is that we have disabled CSRF protection completely, so as to be able to continue development while we are looking for a solution.
Does anyone have any idea what could be going wrong?
The exception Invalid CSRF Token was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN' occurs when an invalid CSRF token is passed. If Spring would receive no CSRF token at all, it would print a different message "Could not verify the provided CSRF token because your session was not found." That means frontend is sending old or invalid CSRF token.
Possibly your corporate network proxy applies some login mechanism and override your X-CSRF-TOKEN header.
According to Spring Security documentation, cookieHttpOnly=false is needed to allow AngularJS to read the XSRF-TOKEN cookie.

Spring, XSRF tokens and performance

I am trying to implement CSRF protection in an existing application. We have Spring MVC on backend and a mix of HTML, CSS and Apache Velocity Templates on frontend.
I have tried configuring the Spring CSRF functionality as shown here - https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html. After reading through this Spring documentation, I get a feel that after configuration, Spring Security would automatically send tokens with all requests but that is not happening in my case - perhaps something is wrong with my configuration.
As an alternative, I am creating an instance of Spring Security's CsrfTokenRepository and calling generateToken and loadToken methods on it in my front controller which intercepts all HTTP requests coming to the server. This way, I am able to deliver a new token for every HTTP request and then send it back on the next one.
The mechanism fails though if I click different links within my page too frequently - by the time a page is rendered and the new token is set in hidden fields, the browser has already sent a request to another page with an old token. It also fails when I open more than one tab since the token received by the latest opened tab wins over tokens from tabs opened before it.
To overcome this issue, I changed the tokens to be generated only per session. However, now I run the risk of tokens being exposed - via get requests or referrer fields for example. Is there a way to improve the performance of per request tokens? or make the approach more secure with per session tokens?

AntiForgeryToken without forms authentication

We have a website that uses MVC3 and a custom authentication method that does not rely on forms authentication at all -- at least from what I can tell. In web.config we set
<authentication mode="None"></authentication>
and we never use/set HttpContext.User anywhere in code. The problem is when using #Html.AntiForgeryToken() in some cases the user gets this error message:
A required anti-forgery token was not supplied or was invalid
We centralize all anti-forgery checks in OnAuthorization with this code:
if (String.Compare(filterContext.HttpContext.Request.HttpMethod, "post", true) == 0)
{
var forgery = new ValidateAntiForgeryTokenAttribute();
forgery.OnAuthorization(filterContext);
}
That is where the exception occurs. We have defined a machineKey in web.config to prevent new keys being generated when the application pool recycles. This did not fix the problem.
Next we thought that maybe the client's browser is not sending cookies. We started logging cookies and noticed that in some cases the RequestVerificationToken_Lw cookie is sent, but in others is not -- even though other cookies, like the ones made by Google Analytics, are sent along just fine. Could it be something in the browser is stripping out some cookies and leaving others in?
It seems like the anti-forgery token depends on forms authentication. Is this the case? Any way to keep using the AntiForgeryToken when not using forms authentication in a reliable way. Keep in mind that the method I described above works for more than 90% of cases, but we can't pinpoint why it doesn't work for some people.
Thoughts?
Thanks!
Do some users have this issue all the time? Or just some of the time? Also, does it work for some of the methods ALL the time or is it inconsistent for the same action method? Do you have any ajax calls? The default anti-forgery token implementation does not handle AJAX calls. But you can write some custom code to get it to work
Are you adding the antiforgery token inside of the form? The antiforgery token is stored on the client via a hidden HTML element so and not as a cookie. The other question would be what browser version are they using? Are can the upgrade to the latest?
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()...

Resources