4

We're developing a Spring appication with Spring Security. After doing some pen testing, one of the test results was a vulnerability:

Cross-Site Request Forgery Token is not bound to user context.

We started to play around a little with tokens in private browsing and such, where we were sure the sessions were all separate and we noticed that the application accepts tokens from any one of these sessions, i.e. we can take a token in one session and simply manipulate another sesson's hidden inputs to use this token, without any complaints from Spring when we submit the form.

This seems to me to be a security vulnerability because (for example), upon finding out that user X is using our app, someone running evil site example.com can also use our app and take their CSRF-Token from his/her session, set up a form on evil site example.com that posts to our app, and lure user X to this form.

I have a hard time believing that I've stumbled upon some unknown security vulnerability in Spring/Spring Security, so what am I missing? Am I misunderstanding CSRF? Is my attack vector not a realistic attack vector?

In terms of security configurations, we've mostly used this part of the Spring Security Documentation to get CSRF configured.

5

So, we've solved it. Spring Security sets the CSRF-Token as a cookie, which evil site example.com can't access because sites can't access cookies that aren't from their domain. Upon submission of forms, there is a hidden input field that has the token and is submitted as well. Spring compares the token in the cookie to the token in the submitted hidden input. If they match, everything is good, otherwise 403 is returned. Since evil site example.com, can't access the token in the cookie, forms submitted from evil site example.com can't have the correct CSRF token in the hidden input (I think DNS spoofing could give them access to the token in the cookie, but then we have a man in the middle attack as well as CSRF)

The problem with our tests above was, that we were changing both tokens: the one in the hidden input as well as the one in the cookie, which, as mentioned above, an attacker can't do, so our attack vector was indeed unrealistic.

Additionally, because of this, Spring Security doesn't need to explicitly bind the token in the cookie to a session because it knows that only our domain, i.e. our App, can change the token (actually I think there is a concept of "session cookies" too, but that didn't come up here), meaning that as long as you're changing the tokens in both the cookie and the hidden input to a valid UUID, the tokens will match on the server side and Spring Security won't care. We even went so far as to use UUIDs for the tokens that weren't generated by Spring Security, meaning Spring Security didn't have them in their tokenRepository and Spring Security still didn't care. This surprised us a little at first, but I guess since the token in the cookie really can't be accessed by the attacker, Spring Security only cares if they match.

  • 1
    The pattern you describe is known as "double submit cookie", and it is very practical since as you discovered it does not require any server state. – Anders Feb 28 '18 at 20:44
0

I can speak to CSRF generally, but not to the Spring framework (which I haven't used). Since I can't verify your claims with Spring itself, I'm going to take your claims at face value. Namely:

  1. You can fetch a CSRF token by making a valid call using any session of your choice
  2. If you force any other user to make a request with your CSRF token, Spring will accept it as a valid token

Assuming that your findings are accurate (again I'm not doubting you, I just can't verify it myself), these are my immediate thoughts:

  1. CSRF tokens should not be allowed to cross sessions like this.
  2. I would certainly consider this a security vulnerability.
  3. Additional details about how Spring validates CSRF tokens are needed to decide whether or not there is a realistic attack vector here.

In detail, CSRF tokens should definitely not be able to cross sessions. Each CSRF token should be associated with the session that generated it, and it should not be a valid token for any other session. As you point out, allowing tokens to cross sessions make it possible for an attacker to generate valid tokens before hand. This can definitely be a problem. However, there are a few circumstances which can mitigate the potential threat here:

More than just token verification

Tokens are just one way to protect against CSRF attacks. Personally, I find them to be the most straight-forward and reliable way. However, they are not the only way. Another option is to check for REFERRER and ORIGIN headers. If Spring is also checking for REFERRER and ORIGIN headers then this weakness in their CSRF token is unlikely to be exploitable. The test you did (manually changing the CSRF token via browser tools) will still leave you with proper REFERRER and ORIGIN headers, which means that you will pass both CSRF checks. However, it would not be possible for an attacker to reproduce this. It works for you because you were on the actual application page and edited the application page directly. An attacker will make a CSRF attack happen from somewhere completely different, and can't force the REFERRER and ORIGIN headers. So if Spring is also checking REFERRER and ORIGIN headers, then you are fine.

Does Spring do both? I don't know. Why would Spring do both? I'm not sure, although it would be in line with the concept of "Defense in depth", so it wouldn't be crazy for it to do both tests. If it is doing both, then obviously even a large weakness in the CSRF token won't matter. If however it does both and ignores one or the other in certain circumstances, then there might be an attack vector if you can make it ignore header checks and also spoof the CSRF token.

CSRF token expiration

There may also be a token expiration that will make it hard to turn this into an exploitable attack vector. In fact, it almost certainly has a token expiration. As an attacker you can generate a valid CSRF token, but if it has a short expiration then you need to get that in front of a target quickly. Say you built a CSRF attack vector that effectively elevated your user account to admin when an admin views a malicious page you generated. This will only do you any good if you can trick an admin into viewing your attack page before your token expires. Depending on your delivery method and the length of the token expiration, this may be very difficult.

To summarize: is this the norm for CSRF? No, in fact it is potentially dangerous. Is it exploitable? That's harder to answer. As always, the devil is in the details. Is it worth digging into more? I would say so.

  • From a subsection of the Spring Security documentation: "We can relax the expectations to only require the token for each HTTP request that updates state. This can be safely done since the same origin policy ensures the evil site cannot read the response" seems to hint at what you are saying about REFERRER and ORIGIN. We'll check this again. We've also done some tests from static html pages that we created to recreate the exploit. If there are problems with them, we'll update here.Thx! – flooose Feb 27 '18 at 20:51
  • Hard to say for sure there. The same origin policy in this case is likely referring to the fact that browser's don't like to talk across domain (aka CORS), which wouldn't prevent a CSRF attack. My own brief google on Spring makes me suspect that they only perform CSRF validation via tokens - not with origin or referrer headers. I would say to just keep digging. It sounds like you are half way to creating an example that would demonstrate the weakness if it exists, so you might as well just keep going. – Conor Mancone Feb 27 '18 at 21:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.