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.
Related
My team is rewriting an existing web application that has a react.js front-end and springboot backend. In addition, The original (legacy) app is written in java (tomcat 8 & struts) and that will continue to be used for some parts of the site until a later date when we will complete the rewrite. All 3 endpoints are on the same domain in the following format: react.js (mysite.mydomain.com), spring (mysiteapp.mydomain.com), and legacy (mysite.mydomain.com/old). All 3 apps are hosted on the same server, but the application urls all route through our F5, so nothing is pointing to localhost. We did this to use the same SSL certificate across the three apps. The new and legacy apps use the same database. We are trying to make cross app calls between the new and old app. When we make the cross app calls, we want the user session to be maintained between them without them having to log in twice. We have not been able to get this to work. In our latest attempt to authenticate the user to both apps simultaneously, we are using ajax to sign the user into legacy with the same credentials. We are getting the following error back from the legacy tomcat application: HTTP Status 403 Invalid CSRF token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'. The server understood the request but refuses to authorize it. We are stumped at this point and out of ideas. Code can be provided on request, but we are looking for the best approach to how to implement this and not as much locked on this path described above.
In my Controller, which is build with using spring-boot, I've enable CORS only for my server and localhost whit this annotation:
#CrossOrigin(origins = {"http://localhost:8080", "https://www.somepage.com"}, maxAge = 3600)
This is working fine. But now I'm not sure, if it's also needed, to add basic authentication for the REST API. As far as I understood, the only call the REST API is accepting now, is my own server and localhost, and that's why, I think it's not needed. But I was not able to figure out, if this is a bad practice or not.
Do You recommend to use basic auth too for the REST API even when CORS is enabled?
No.
The Same Origin Policy is a feature built into browsers that prevents an attacker's JavaScript running on the attacker's website from reading the response to an HTTP request from the victim's browser to the targetted website.
This stops the attacker from stealing data from the targetted website using the credentials belonging to the victim.
(To some degree. There are other kinds of attacks.)
CORS is a tool that relaxes this rule so that when you to allow another site to access that data (either using the user's credentials or because it is just public data), it can.
Note that I said "a feature built into browsers". It isn't built into other tools.
An attacker can still make HTTP requests with their code, or tools like Postman and curl, or their own web browser.
Neither the Same Origin Policy nor CORS are substitutes for authentication and authorization.
CORS is a mechanism implemented in browsers and it will not prevent me to access your API with curl. Therefore, secure your API if you need it to stay secure.
I have an Angular App with a Spring Boot Backend. I use "azure-active-directory-spring-boot-starter" for the authentication via Azure AD. Everything works fine locally.
After the successful external (azure) login "https://local.../login/oauth2/code/azure?code=..." is called.
The request headers on this request contain authorization cookies from azure and the response header contains two "set-cookie"-entries: JSESSIONID, XSRF-TOKEN
After that request the user is logged in as expected.
With the production setup however it seems, that cookies are not set successfully.
The application runs on a Tomcat 9 behind a apache reverse proxy. The azure login itself is successful.
Also the request headers contain the two cookies, however "set-cookie" is not set. Any ideas how to solve this?
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.
So we have a platform secured with a JWT access-token. To access platform REST API, we send the token in a Bearer Authorization header; when fetching static content (such as SPAs' code, CSS, fonts, or even for jumping between SPAs) we send the token in a Cookie since we have no control over those requests. We were OK so far...
At some point in time we added Spring Security in our APIs. This made some things easier for us but SonarQube displayed warnings for disabling Spring Security's CSRF protection (when configuring the framework we thougt pfff, we use headers to transport our JWT, we don't need CSRF protection for our APIs), but we tried to solve the warning anyway.
Investigating we came across SameSite attribute for Cookies and we are kind of confused if this would solve all of our problems.
So the question would be: is SameSite cookies' attribute enough for CRSF protection?
If so, is there a way to let Spring Security / SonarQube that we're OK against such attacks?