TL;DR OAuth2 sucks.
Please don't think about OAuth2 as about the next generation of OAuth1. They are completely different like colors: OAuth1 is the green version, OAuth2 is the red version
The biggest OAuth1 provider - Twitter.
I bet ($100!) they are not switching to OAuth2 in the near future. Pros and cons:
+ becoming compatible with the rest of social networks
- making authorization flow insecure, like the rest of social networks
I am not telling OAuth1 is super secure — it was vulnerable to session fixation a few years ago. If you made user to approve 'oauth_token' issued for your account, then you could use same oauth_token again and sign in his account on the Client website.
It was fixed in oauth1.a. Wait, read again: it was fixed. None of oauth2 vulnerabilities i pointed out in my previous posts a year ago was adressed in the spec. OAuth1 is straight, concise, explicit and secure protocol. OAuth2 is the road to hell.
Here we go!
OAuth2 core vulnerabilities - parameters
I have no idea why, who and, generally speaking, what the fuck, but we can transfer "response_type" as a parameter in URL (not as a setting of your Client).
Vector 1. Set respones_type=token in authorization URL and use specially crafted redirect_uri to leak access_token from URI fragment. You can add Chrome vulns like we did to hack Facebook. Hashbang #! is very nice bug too. Controlled piece of code (XSS) or mobile app like Nir did. BTW avoid pre-approved Clients ( Facebook Messenger with full permissions)
response_type must be a constant value in application settings.
Vector 2. If spec was implemented properly then tampering redirect_uri to other, "leaky", values is pointless. Because to obtain access token you must send redirect_uri value with client creds. If actual redirect_uri was "leaky" and not equal real redirect_uri Client will not be able to obtain access_token for this code.
Vk (vkontakte) was vulnerable to this attack until Sep 2012 - redirect_uri wasn't required to obtain token. An <img> on client website could leak victim's 'code' through referrer and attacker could use same 'code' to log in victim's account.
redirect_uri should be a constant value in application settings.
'scope' (actions User should allow for your Client) is also transfered in query string. I can't call it a major vulnerability, rather ugly user experience but anyway this is sort of stupid, no? "Pay as much as you wish, up to you"
Checking permissions after obtaining access_token is barely a right solution. This is rather a work around. Much better:
- if it's a parameter in URL then User should be able to remove some permissions by clicking [X]. This is my human rights, fuck yeah!
- if you really need some scope so badly - set it in Client's settings. Now you are 100% sure that User granted these permissions.
Authentication vs AuthorizationWiki
authorize = permit 3rd party Client to access your Resources (/me, /statuses/new)
authenticate = prove that "this guy" is "that guy" which has account on the website.
OAuth2 was designed for authorization purposes only, but now in 90% (yep!) of cases people authenticate with it too! They simply use /me endpoint and find a record in database by external_user_id and provider_name.
It leads to bad things happenning.
Vector 3: The Most Common OAuth2 Vulnerability or how I found CSRF based account hijacking vuln in most of OAuth2 implementations (omniauth, django etc)
Any implementation w/o 'state' is vulnerable to CSRF. It leads to session fixation (Alice is logged in as Mallory) and account hijacking (Mallory can log in as Alice because Alice connected Mallory Provider Account).
Vector 4: One access_token to rule them all or make sure access_token belongs to your Client
I truly love signed_request (Facebook feature!), it provides information about Client that issued this access_token. I think we should stop using response_type=token and use signed_request only. Because it is a reliable way for mobile and client side apps to obtain access_token+client_id of this token+current user_id (less round trips).
If we make redirect_uri constant and introduce encrypted_request (same data but encrypted with client_secret) it would be just perfectly secure flow.
XSS Threat, Vector 5
Nowdays XSS cannot steal user's session because of httpOnly flag (I assume everyone uses it).
Wait.. does OAuth2-based authentication have any protection from possible XSS on Client's website? No, attacker gets carte blanche:
- create iframe with response_type=token in authorize URL, then slice location.hash containing access_token
- use response_type=code and set wrong state. If CSRF protection is implemented 'code' will not be used and attacker can hijack User's account simply using this 'code' (within 10 minutes). Friendly reminder for Providers: 'code' must be used only once and within couple of minutes — I got $2000 bounty from facebook for finding replay attack.
- if csrf protection is NOT implemented (wtf?!) you can try a cool trick - 414 error. Set super long state (5000 symbols), facebook has no limits on state but Client server most likely has. 'code' won't be used because of server error - slice it!
- 'state' fixation + using callback with Attacker's code to hijack account
- always obtain access_token for every received code. But don't use it if state was wrong. "Destroy" code, or attacker will use it.
- clear state cookie as soon as it was used. "Destroy" state or attacker will use it.
Phishing Threat, Vector 6
Can you imagine how simple phishing is: create a Client with slightly different name: e.g. Skype -> Skype App/Skype IM, copy picture, description from original Client, use skype.com.evil.com domain.
- Verified Clients. Similar popup near Client name, like we have for twitter accounts.
- Add information about amount of Users this app has. I won't authorize "Skype IM" having only 12 users with such app installed.
Client Leaked All the Tokens.
What is Provider gonna do if some Client leaked all access_tokens at once (SQL injection for example). I think there should be kind of Antifraud system, monitoring token usage. And I am developing one btw, let me know if you wish to try.
I might be missing something, but why can't we use access_token + client creds to obtain a new access_token? refresh_token = access_token. If access_token is stolen it cannot be refreshed w/o client creds anyway. If client creds are stolen too = Game Over.
Dear Providers, please, add valid_thru parameter, I want to set for how long the Client has access to my Resources.
Last but not least threat, MITM
SSL and encryption. OAuth2 relies heavily on https. This makes framework simpler but way less secure.
so, OAuth...?This is a sad story. And I don't know what to do and how to fix it.
Who to join? OAuth2.a? i am just a russian scriptkiddie nobody listens to :/
Framework, not protocol, they said.
coming a mess big frameworks authorization in
Your blog has been added to the InfoSecMash.com newsfeed and our Google Currents #InfoSec magazine.ReplyDelete
I thought the web was safe - before I found this great blog. Keep posting! Very interesting articles.ReplyDelete
lol, best compliment ever!Delete
The web is generally unsafe but not for the mere reasons mentioned here, this is more about implementation of specification and not specification itself. The author is being very disingenuous with his assertions here.Delete
@anonymouse, just read the specification and come with some arguments next time.Delete
"Stolen access_token = Game Over."ReplyDelete
Ummm no. The whole point of OAuth2 tokens is that they're very short lived, so having one leak isn't really 'Game Over". Getting your hands on a Refresh Token would be "Game Over".
I feel your frustration with the OAuth2 spec, but ultimately it comes down to implementing it with best practices in mind. A secure OAuth2 implementation won't allow arbitrary redirect_uri's to be used, will have very short lived access tokens, will use refresh tokens, and will only allow response_types that make sense for how clients and users aught to behave.
Most of the other vectors you explain are not caused by OAuth2 at all, but are associated with a poor implementation of it. Is it really fair to blame the OAuth2 spec for not escaping HTML? For users getting phished?
A mitigation for the phishing threat, by the way, would be to show the user who created the client and prove that they're a "trusted vendor" or something.
>Ummm no. The whole point of OAuth2 tokens is that they're very short lived, so having one leak isn't really 'Game Over". Getting your hands on a Refresh Token would be "Game Over".Delete
ha, try facebook and response_type token. access token you will get will never expire
>A secure OAuth2 implementation won't allow arbitrary redirect_uri's to be used
how to make a "secure" oauth2 impl.? Is there "Making it Secure" paragrpah in spec?
> will only allow response_types that make sense for how clients and users aught to behave
it's cool to have only needed response_tokens, but wait, if i need both code/token do i still can set it as URL param? Maybe here is the problem - it must not be a param anyway and spec should explain it.
>t. Is it really fair to blame the OAuth2 spec for not escaping HTML?
Oauth2 now claims to be wide used authentication tool. No way back. And it should try hard to help clients, even if it's client's fuckup : XSS, CSRF w/o state etc.
>A mitigation for the phishing threat, by the way, would be to show the user who created the client and prove that they're a "trusted vendor" or something.
yeah, that's what i proposed - verified clients.
leaking access token is game over for short lived too.
If it is still active who cares it is active for 2 minutes or forever? I only need 5 seconds to spam on your wall and steal your friends graph. Short lived != safe
@Egor, FB Access Tokens expire in about an hour (you can extend it to two months but you need app secret to do so). However, I agree that you just need seconds to fetch user's e-mail, private data and minutes to even get whole user's stream history.Delete
double checked - 3687 secondsDelete
i recall a few months ago it was 0!
anyway, it's perfectly enough to do bad things
I've got to give credit to this point "If it is still active who cares it is active for 2 minutes or forever?"Delete
The argument of "... they're very short lived, so having one leak isn't really 'Game Over'" sounds more likely from developers rather than from a security professional.
There are multiple things that have been implemented to counteract this situation, control of origin, and more.Delete
Completely moot point, this is more on implementation than specification. Your argument should be instead especially as a "security consultant" to better implement the protocol chosen, not that the protocol is bad because people implement it improperly or choose poor implementations.
Lets say thousands of mechanics get the same car blueprints and build the car using the exact same parts, 99% of the mechanics build a car that works beautifully and is a sight to behold, 1% do shoddy work and the car is prone to breaking down often.
Are the blueprints bad or are those who implement it poorly bad?
I would advise to avoid this posters blog in the future as someone who does security consulting myself.
much more than 1% of implementations are done wrong. And specification sucks because it allows insecure defaults. Specification must enforce same rules for everyone, not just put outlined bugs in "threat model" and leave it up to implementation developers.Delete
"use response_type=code and set wrong state. If CSRF protection is implemented 'code' will not be used and attacker can hijack User's account simply using this 'code' (within 10 minutes)."ReplyDelete
Since you don't have app secret you can't do it (https://github.com/facebook/facebook-php-sdk/blob/master/src/base_facebook.php#L775). I didn't check but I'm pretty sure code is bounded with specific app id. Also, how would you obtain this code anyway? If site has X-Frame-Options: DENY you can't embed it in iframe. If it hasn't, you still can't get this iframe URL after redirection.
>If site has X-Frame-Options: DENY you can't embed it in iframeDelete
so what, window.open
> Since you don't have app secret you can't do it
I don't need app secret. I simply use exactly same code on same callback as an attacker within 10 min
> so what, window.openReplyDelete
Again, you can't get query param from URL if window has redirected. Am I right?
> I don't need app secret. I simply use exactly same code on same callback as an attacker within 10 min
OK, but how would you obtain it? Man in the middle?
this is part of "XSS Threat" paragraph. when I find XSS on client I open window for every user, with either wrong state or very long state to make 414 error.Delete
code will not be used, redirect_uri will be proper. Later i just use same code by myself
>Again, you can't get query param from URL if window has redirected. Am I right?
no, it's possible — you can try code from the post - in fact there is a few seconds when you can copy it
1 xssed page opens authorize urlDelete
2 authorize url -> callback url with code and state
3 now website of client with wrong state or very long - code is not used so XSS steals it
If you "find xss on client" meaning that the implementation was done poorly.Delete
OK, I missed that it's XSS part. Thanks!ReplyDelete
at Vector 5, can you write explicitely, which part is done on the attacker side, which part is done on the Victim's side via XSS/CSRF? I'm confused....thanks.ReplyDelete
XSS is on Client's website, for example, fbapp.com. All code is executed there as well - stealing code/token and CSRF token stealing tooDelete
also at 4:
"I truly love signed_request (Facebook feature!), it provides information about Client that issued this access_token. I think we should stop using response_type=token and use signed_request only. "
That signed_request, what info does it provide about the client? I though it's purpose is for the client to verify that the response came from facebook or not.
yes, but at the same time it provides user_id property - you need less round trips to authenticate current user.Delete
you should check singature ofc
You owe all of us 100$ https://dev.twitter.com/docs/api/1.1/post/oauth2/tokenReplyDelete
ha, this is not auth code flow, only client creds grant typeDelete
By the way even if (installed app) clients are verified and signed, the clients themselves are still vulnerable to reverse-engineering. So if the "client secret" is stolen, what to do?ReplyDelete
yes, i can be also mitigated (but it is not).Delete
Stolen Client Credentials Threat
shortly - if redirect urls are whitelisted and not forgeable stolen credentials is not a problem
That are the reasons why the original author of OAuth 1 resigned from the project: http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ReplyDelete
But meanwhile there is http://openid.net/connect/
which is nothing but an oauth2 wrapperDelete
The whole "transition" (pardon my french) from openid to openid-connect-vapour (which - like you say - is OAuth2 with an OpenID name tag) by closing down myopenid first is shameful for janrain and everyone else involved in openid.net.Delete
Longer rant: https://dubiousdod.org/go/OpenID
It doesn't mean good old openID is dead though (you can't kill an idea). For example - combine an own-openid server (that's how I logged in here) with the cretificate-free domain-authentication every i2p user has out-of-the-box, and hellllo darknet-trust-management.
Any reason why attack 5 can't be used with Oauth 1? You said they fixed the session fixation in 1.0a with oauth_verifier. But it's still vulnerable to your attack:ReplyDelete
The provider redirects the user to the client's callback url with oauth_verifier. The attacker steals it from the iframe url, and uses it on the attacker's client account, which is then connected to the victim's provider account.