0

Setup

  • Restful API and thin JS web client
  • Login POST Endpoint using parameters email and password
  • Access-Control-Allow-Origin is set, so responses can only be retrieved from non attacker website. However, as per specs, a pre-flight request is not issued to the Login endpoint.

The Attack

A malicious website can make requests to the Login endpoint without the user knowing. The response can not be retrieved by the browser, however the requests still go through.

Why is this bad? At the very least the attacker can rate limit the users IP address. If they target a specific email, they can make it look like a DDOS using many (real user) ip addresses and lock out the email. If they have access to the network traffic they can even analyse the response package size to determine if a login attempt was successful (Distributed Brute Force).

Sort-of-solution

What we've done so far is adding a xss token that is tied to the requesting IP address. The token is first retrieved through a separate endpoint and then required to be submitted to the login endpoint. The login attempt is made only if the token is valid and matches the requesting IP. This prevents the above attack.

This unfortunately only works if the users IP doesn't change between requests. This is now becoming a problem as some users are using services like Tor or Onavo. These users can no longer use our system.

Additional Information

  • We can't use browser cookies or browser local storage
  • Browser might not send referrer if so configured by user
  • Here is an attack that would work if the token was not tied to the IP address: enter image description here

Question

Should we worry about this attack or just drop the xss token requirement? Is there a better way to prevent this attack? How do other services solve this?

vincent
  • 171
  • 4
  • 1
    XSS tokens? You mean XSRF (CSRF) tokens? Even if your users use Tor, the IP is not changed in every request because it will use one exit node until the Tor user decides to change this. – Jeroen Sep 12 '17 at 17:16
  • Sure, name of the token isn't that important. It's not to prevent a Csrf attack, so calling it Csrf token is misleading. – vincent Sep 12 '17 at 17:18
  • Ok, can you please elaborate a little bit more on what you mean by an XSS token? Session token? – Jeroen Sep 12 '17 at 17:20
  • 1
    IP change on every request is exactly what we are seeing from Onavo. So this is not a theoretical use case. I'm pretty sure you can configure Tor to do the same. – vincent Sep 12 '17 at 17:22
  • XSS Token = encrypt(requesting IP, secret, salt, timestamp) ---- the server can then decrypt the token and match the encrypted ip with the ip making the login request – vincent Sep 12 '17 at 17:24
  • I'm not an Onavo user so I can't say anything about that. I do use Tor and although it is possible to do this, it is not default behavior. I think you should ask yourself if you want your users to be able to work with your system if their IP addresses change every request. – Jeroen Sep 12 '17 at 17:25
  • Agreed, however there appears now to be a substantial amount of users with this problem. I'm really curious if (a) this is an attack that is usually considered and (b) what other companies do to prevent this – vincent Sep 12 '17 at 17:30
  • Instead of using the IP - what about encrypting the username instead? – Jeroen Sep 12 '17 at 17:30
  • The problem here is that the attacker can then generate the xss token and send it to the user. This defeats the whole purpose unfortunately. – vincent Sep 12 '17 at 17:32
  • Well, that's assuming the attacker knows the username. How about this. Generate a secret token upon registration which is stored in your back-end system(s) and never displayed to your users and use that as one of your secrets during the encryption process. – Jeroen Sep 12 '17 at 17:37
  • Could you elaborate a little how this would prevent the attack(s) outlined in the question? – vincent Sep 12 '17 at 17:40
  • Apparently I misunderstood, I thought it was an issue after authentication. After re-reading your message it's a token that you want a user to retrieve and submit during the authentication process. Isn't this what an anti CSRF token is for? That way the system can determine the origin of the request. – Jeroen Sep 12 '17 at 17:49
  • Yes, but an (anti) CSRF token is designed to be used with authenticated users and tied to the user session. So the (anti) XSS token is now tied to the IP instead of the session (because we don't have one yet). However with the IP changing this becomes problematic as described. – vincent Sep 12 '17 at 17:55
  • Many security consultants, including myself, believed that anti CSRF tokens were meant for state changing requests only (authenticated). However, I have changed my opinion on that. Generating an anti CSRF token on a login page will prevent the issues you describe. Using such tokens is only to determine the origin of the request. Anti CSRF tokens can be implemented in two ways, session based (as you described) and also request based. I think implementing this would solve your issue. – Jeroen Sep 12 '17 at 18:01
  • Sorry, you totally lost me there. I'm not sure how CSRF tokens apply to the problem at all. Can you please describe when a token with what information is generated and how it's used in the auth flow. – vincent Sep 12 '17 at 18:07
  • In your "Sort-of-Solution" you state: "The login attempt is made only if the token is valid......" So at the login page, a token (anti CSRF) is generated and sent in the response of the login page. The token can be placed in a hidden input field. The user enters credentials and sends the POST request, containing the token, username and password. Server side the token is checked, if the token is valid, verify the username and password. If the token is incorrect stop the authentication process. – Jeroen Sep 12 '17 at 18:15
  • Correct. But this does not prevent the attacker from generating the token themselves and then serving it to the user through the malicious website. A token once generated can be used by who-ever wants to use it. We can tie it to the username which might improve security slightly but doesn't prevent the issue. – vincent Sep 12 '17 at 18:28
  • 1
    Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/65498/discussion-between-jeroen-it-nerdbox-and-vincent). – Jeroen Sep 12 '17 at 18:31
  • You could block any login requests that don't have an Origin header or Referer header referencing your site's domain. (That would however block some people who are using browsers too old to send the Origin header that also manually disabled the Referer header. I think the overlap of old browser and very uncommon settings isn't worth worrying too much about as long as you have a good error message.) – Macil Sep 12 '17 at 22:47

2 Answers2

1

tl/dr Your setup assumes that an attacker can't read requests from your server. This is only true for in-browser requests. However, a login endpoint doesn't use cookies so is not authenticated and therefore an attacker has no reason to be making requests in browser. As a result a core assumption you are making (that the attacker can't read responses from your endpoint) is invalid, and this security step is largely useless. Now that it is causing trouble for your end users, you should ditch it all and stick with more common brute-force monitoring. Off-the-cuff security measures tend to create more problems than they solve - stick to the basics. I don't think anyone attempts the kinds of attacks you are worried about anyway. A full site DDoS is probably easier, so no one would bother trying what you are worried about.

There is definitely some confusion of terminology going around that I think is worth addressing. Most importantly, you keep using the term XSS but this is not an XSS attack. I agree, your concern isn't exactly a CSRF attack, but it definitely isn't an XSS attack either. The reason is because you seem to be under the assumption that such malicious attacks would come from a browser: in a case like this they absolutely wouldn't (which is why this isn't a XSS attack).

This is more than just me being pedantic. You mention in your original question that about Access-Control-Allow-Origin and other CORS settings, and state that a malicious user could hit your endpoint but not read the response. Again, this is only true from requests made from a different domain in a browser, but in a case like this there is absolutely no reason why a malicious user would be using a browser. The only reason you would work from inside a browser is if you are trying to take advantage of a user's cookies for authentication (which is the case for CSRF attacks). however, this is a login endpoint, which means there are no cookies, and presumably therefore no reason why the attacker needs to be doing anything from inside a browser. That is to say:

A malicious attacker could use any HTTP client to make these login request, they wouldn't be affected by CORS rules, and would absolutely be able to see the response from your server

As we'll see, this fact invalidates the assumptions you are making about your security, and the end result is that your chosen security step is not actually securing anything. A malicious attacker hitting a login endpoint doesn't need to worry about pre-flight responses, CORS, access control headers, or anything else, because those are only relevant to HTTP requests made by javascript running in a browser, and they would most likely be using scripting tools making HTTP calls from outside of the browser.

Back to your question

So you have this extra security step in place to (effectively) try to prevent malicious attackers from attempting various DoS attacks on your login page. You specifically mention these concerns:

At the very least the attacker can rate limit the users IP address. If they target a specific email, they can make it look like a DDOS using many (real user) ip addresses and lock out the email. If they have access to the network traffic they can even analyse the response package size to determine if a login attempt was successful (Distributed Brute Force).

As stated, your current step is not actually protecting against most of this. In particular, there is absolutely nothing stopping a malicious hacker from setting up scripts on dozens of different of ip addresses (even just rent-a-vps from a cheap hosting provider), hitting up your GET endpoint to get an access code, and then (with the same IP of course) hitting up your login endpoint with that access code and a chose user's email address. This will look like a DDoS of a user's email address (because it is) and from the sounds of it will result in that email address being locked. They don't need to bother analyzing network traffic to see if the response is successful because they will be able to read the response themselves. Again, it sounds like you were assuming no responses from your server could be read, but this is only true for browsers, and does not impact HTTP requests from any other HTTP client.

Your security will prevent a malicious attacker from performing a DoS with the user's IP address (which could happen if the attacker can get the user to visit a page that has javascript they control), but that doesn't matter. At that point in time you are talking about a malicious attacker that is clearly targeting your system and/or users, and as such the attacker will have no trouble figuring out how your login flow works and performing the above attack to lock out an email address or even brute force the password.

All that to say: regardless of whether or not this attack is a general concern on the web, the method you have chosen to defend against it and which is causing trouble for your customers is not actually providing any real protection for your system at all. As a result, the overall answer is easy:

Ditch this security step and make life easier for your users

You also asked:

Should we worry about this attack?

The answer is a simple no. Technically things are rarely yes/no in security, and the answer is always "It depends on the level of risk you are willing to accept and your expected threat model". However, unless you have reason to specifically expect this kind of attack in the near future, I would say that it is not a concern at all. I've never heard of such an attack happening in practice.

You should have monitoring to try to detect and block brute force attempts against your login page, and you should tie that protection to IP addresses (the details vary, but in essence it is very reasonable to start blocking repeated failed login attempts for the same ip address). However, I don't think you have to worry about a malicious attacker getting javascript in front of a user to get them to black-list their own IP address (which is essentially the attack you are worried about). While that is certainly possible, I don't think I've ever heard of such an attack happening in practice - it would have to be a targeted attack and the gains would be so minimal, I doubt anyone would bother. If someone really wanted to lock people out of your system they would probably have better luck (and an easier time) just doing a rent-a-DDoS and completely shutting down your entire site anyway.

Conor Mancone
  • 29,899
  • 13
  • 91
  • 96
  • 1
    @JoshuaJones Done - sorry, I forgot that I can be very long-winded – Conor Mancone Jun 29 '18 at 14:27
  • Excellent answer. It could also be worth explaining to the OP what an XSS attack exactly is, since he misuses the term, but as you brilliantly explain it has really nothing to do with the question asked. – BgrWorker Jun 29 '18 at 15:36
0

The attack you are describing seems to be a CSRF attack. And the token you describe seems to be a CSRF token. These tokens are generally not tied to an IP address (for reasons you describe) but rather they are associated with a session (or session / page request).

Even login pages generally have CSRF protection to protect against Login CSRF.

Egret
  • 436
  • 3
  • 5
  • Take a look at the difference between Csrf and xss attack. The browser has no information stored in our case, so this is not a Csrf attack. I agree there is a strong analogy between our approach and how Csrf attacks are prevented. However this does not mean that this is a Csrf attack. Session information is never stored in our case so it's (a) not part of an attack vector but can also (b) not used in any prevention mechanisms. – vincent Sep 15 '17 at 14:22