28 Aug 2012

OAuth2: One access_token To Rule Them All




One access_token to rule them all...
I demonstrated The Most Common OAuth2 Vulnerability in Authorization Code Flow a while ago – signing in the Victim's account(followed by getting an access to Victim's resources on that website) by connecting Attacker's oauth account through CSRF-callback URL with Attacker's code. Despite popularity of that attack, it can be easily mitigated with 'state' param(to prevent CSRF) – and now it's fixed in popular ruby/python libraries.

Now I want to share a very straightforward attack for Implicit Flow based websites(realized in brainstorming with @isciurus). There is no proper mitigation so far.

Generally speaking:
  1. 'access_token' is a string that identifies your Resources on Provider. No other parameters are required to call Provider's API. There is no guarantee user's access_token1 for client1 is not used by client2 or client75 - nothing stops them.
  2. Let's assume I create a website: e.g. superfunnypicturez.com. I ask my users to authorize my Client on facebook and I don't ask any scopes at all - I need only "/me" endpoint with "uid" param to be available. They authorize my Client because superfunnypicturez.com requires only 'read' access and nothing seems to be dangerous. I get their 'access_token's and now I can request /me endpoint and get their "uid"(often used for authentication)
  3. Let's assume there is another site: e.g. weuseimplicitflow.com and yep, it uses Implicit Flow(receives token via CALLBACK#access_token=123qwe...). It's a pity - this website authenticates users by given access_token. Most likely it sends access_token on server-side and invokes /me endpoint from there.
  4. Since I am admin of superfunnypicturez.com and you authorized my Client and gave me an access_token from your account I just put that access token in callback URL: CALLBACK#access_token=YOUR_TOKEN and now weuseimplicitflow.com's Client authenticates me as you because that token I just provided returns your 'uid' when /me endpoint is called. I need only one access_token from your Provider's account to rule all your accounts on 3rd party websites that use Implicit Flow.
Recap:
OAuth2 is extremely insecure for authentication goals by default. 
  • Auth Code Flow: You must use 'state' parameter and verify - is this user the same user you sent to authorization URL by checking state value from session and returned one.
  • Implicit Flow: You must ensure that access_token you are going to use is issued for your Client. Please, use this URL for Facebook: https://graph.facebook.com/app?fields=id&access_token=TOKEN. Not all providers support this feature though. If access_token is anyhow obtained from User (not from Provider) - you must verify is this access_token issued for your Client.
Oh, also:
As I mentioned in my previous post on stupidity of OAuth2 - if Provider has Implicit Flow as an option - lots of Users can be compromised someday. If a single XSS is found on Client's domain - hackers can steal all access_tokens with it even if your website uses Auth Code Flow by just changing response_type param in authorize URL. 


OAuth2.a - simple and secure. Join! Honestly, yeah, OAuth2 is Road To Hell. Deal with it. But OAuth2.a is road to Simple and Secure both Authentication and Authorization. I am implementing Provider - charm. If you trust me and have a popular RoR website that needs OAuth - ping me, I will help you to introduce OAuth2.a and make your API users happier.
Author of this article is awesome - you can actually hire him.