Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Vidio, an online streaming platform with 100 million monthly active users, the researchers found that when logging into the site through Facebook, the site did not verify the token — which the website developers and not OAuth must do. Because of this, an attacker could manipulate the API calls to insert an access token generated for a different application, the researchers found.

How does this work? I thought the token was an opaque hex string and only had meaning when sent back to the original server?



The token can be a opaque string but doesn't have to be. Often, it's a JWT which can be decoded, validated, and used locally without a trip to the auth server.

It looks like Vidio, etc validated the signature of the JWT properly but didn't check the "aud" or audience claim, which defines who should use it. Therefore, it was a valid token, it was for the target user, but the token itself wasn't for Vidio.

It's the equivalent of buying a ticket for a United Airlines flight, going to the airport, and United letting you get on ANY flight because you have a valid ticket.

Background: I helped build the Okta OAuth product (not related to the breaches) and I'm the author of the top OAuth/OIDC course on linkedIn.


Heh, this is exactly how I got on the wrong flight once (the gate changed and I didn’t realize it), the machine didn’t tell the person I was getting on the wrong flight and the attendant didn’t notice.

The only reason I discovered I was on the flight is because someone else had a ticket for my seat. The security people were very curious how I did it, both pilots were very pissed (both flights had to be stopped from leaving the gate until they were sure I didn't leave a bag on one of the flights, yay terrorism). But now you can’t do that any more.


Not just aud, but also issuer and you should check it was signed by a proper certificate no?

https://learn.microsoft.com/en-us/entra/identity-platform/ac...


Depends. The core issue is that a lot of developers confuse authentication and authorization. And oauth kind of muddles the water on that front by vaguely doing both but not really. All it does really is exchange tokens (assertions).

Anything JWT related is orthogonal to oauth. Oauth does not actually provide any semantics for the tokens you exchange. They might be JWTs and you might be able to verify them. Or they might just be some random number, a hash, or some kind of database id. But you would have to know in advance what it is exactly and what the proper process for verifying the token is.

In the case of a random number or some kind of session id, the process of verifying that token would involve looking it up from some kind of database or via some kind of API.

Alternatively, with a JWT, it might be a signed one (hopefully) and you'd be able to check the signature if you have the public key. All you have at this point is a blob that was signed by someone that you might or might not trust. The claims and other meta data in the token would tell you hopefully who the person is (authentication) and the level of access they might be given (authorization). The JWT spec defines some fields that you might use for that. But it's up to you to check all those things and verify them.

The point is, that a lot of this stuff is simply implementation/vendor specific and you just need to know what to check and how and why. And it's on you to do all those things.

This is not an issue with oauth, or JWTs, but with incompetent developers doing things wrong or being lazy/negligent/indifferent. Which sadly is very common. Lots of people that get busy implementing all sorts of stuff without any formal training. And a lot of this stuff isn't even taught properly in schools/universities.

This falls in the same category as putting passwords plain text in a database, not setting up ssl certificates, running your database on an open port on the internet, and similar things that developers do wrong all the time. Because they don't even know the basics of how to do things right. A lot of these things would be caught during any half decent audit. And quite a few of those things could expose the companies involved to some expensive law suits. Which is why lots of companies spend money on audits because they don't like expensive law suits.


> incompetent developers doing things wrong or being lazy/negligent/indifferent. Which sadly is very common.

I was code reviewing a dev who was implementing some auth and I mentioned they should log something for audit purposes. They said 'auditing isn't one of the requirements of this ticket' ... at which point, I knew it was time to leave. Like do you need someone to write a ticket to do your job? When people come ask "who logged in and deleted my org's content" ... you better have an answer. This is software engineering 201: logging.

It's like an electrical engineer wiring a house not up to code and saying 'I don't care because the customer didn't ask for it to be up to code.'


I get what you're saying, but... Logging is not auditing, AFAIK.

Also, and I may be very wrong based on the way that org prepared tasks, he may have been signaling scope creep... If it's not in the task, you don't do it. If you think it should be done, you backlog it...


Not to be pedantic, but it's called an "Audit Log", usually. So, it is logging, though if you don't keep your logs forever, it is usually logged to a db or something (but that isn't necessary -- depends on the industry, privacy policy, etc.).

> If it's not in the task, you don't do it. If you think it should be done, you backlog it...

You shouldn't have to be told to do your job. One day, someone is going to ask why X wasn't done (when it clearly should have been, by all industry standards, and especially when it is exactly one line of code) and "I was just following orders" doesn't work in any court, including the ones that get you fired.

When implementing auth, almost no PM is going to write down "mitigates timing attacks" as part of the acceptance criteria, but you do it anyway because it is the right way to do it.


And none of those things come down to the individual dev in any sizable organization I've ever seen. Suddenly you've started adding a db schema new db schema to the requirements and are likely blowing up the sprint.

You push the ticker back up and fire off a few email to PM and ask what the hell they are doing with the requirements


Who said anything about db schemas? What are you talking about?


Where you throwing your audit data? Kibana?


That's an implementation detail so far from what we're talking about that its almost nonsense. Even if you don't have any infrastructure at all, simply injecting a NullLogger and audit logging to it is better than nothing at all (and filing a ticket to address that ASAP). That's two lines of code, but what I was talking about was exactly one line of code because the infrastructure already existed.


I agree with the comment above yours but also feel that this is... different style of work? Or attitude? Not sure how to name it but feels like I also brushed up against this numerous times.

I assume that line of "what is scope creep and what is assumed to be in scope even if not written down" has to be blurry but I tend to judge people on which side they end up on, because I feel it correlates to indifference etc.

Ultimately, it feels like a bad approach to work relationships - you actually have to do personal groundwork in your team to unblur the line to where you and your team agrees. And if that doesn't work out - leaving is a valid, if sad, option.


> he may have been signaling scope creep... If it's not in the task, you don't do it. If you think it should be done, you backlog it...

Correct. Especially if you’re working for a client that’s billing by hour. Regardless, new work changes scope and blah blah agile. Yes it’s overhead, and it also creates a chain of who did what, and when.


I fail to see how adding "$this->auditLog->info('user logged in', $details)" is scope creep. Maybe you can help me out?

This is no different than suggesting a change in the code (in PHP) from:

    $password === $expectedPassword;
to

    hash_equals($expectedPassword, $password);
where the hash_equals function is a timing-attack resistant comparison.


While what you're saying applies to OAuth in general, there are a lot less variables in this situation.

This was specifically "Sign in with.." so we know it was OpenID Connect which mandates signed JWTs for ID tokens. Those JWTs would have an issuer (the social provider) to determine their source and the client id (to determine the intended app) so those aren't in question here.

Further, since these are ID tokens, they WOULD provide authentication information (technically identity info) and minimal authorization info.


Nope.

The thing about cryptography you always hear being said is "don't roll your own crypto", because you don't know all the complexities and failure modes that experts have researched.

In o-auth, just you comment mentions multiple ways of doing things, and different responsibilities the developers have in different circumstances. Essentially the developer is forced roll some of their own auth.

Compare that, for example, to say how you use TLS. You just use the standard libs bundled everywhere, and open connection, and the the system does all the validation for you. The complexity is there, but average developer is not expected to understand it.

O-auth simply exposes too many details to the average developer, and too many options to use them differently, for it to ever be secure.


That's just the way oauth works. I agree it's a messy protocol. Classic design by committee stuff. Exactly what you get when mutually incompatible security product vendors try to create a standard that covers what all of them do.

Unfortunately, there are no good alternatives that don't have this problem. Basically people use all sorts of commercial products in the hopes that it will act like magic security pixie dust. Most of them are super complicated to work with. Or expensive. Or both.

There's just no way around the fact that you need to know what you are doing and how stuff sticks together.


While what you say is true about OAuth2, this isn't what "don't roll your own crypto" refers to, I think. It's more like "don't start implementing crypto routines yourself".


Indeed.

In my case, I was initially confused by the fact that in most examples (specifically for Azure AD, which is what I've used), they give you an access token for another app, here MS Graph API. It specifically says you're not supposed to read that access token, so you can't validate it. You should use the ID token instead (which can be validated), but in the default configuration, the ID token doesn't carry much information, so you need to call the `userinfo` endpoint with said opaque access token.


For Microsoft it's another case of them going out of their way to exercise every possible inch of lee-way permitted by the specifications. No one in their right mind should expect a userinfo endpoint to only be accessible at an entirely different host. It's doubly stupid that Azure AD provides no other options for remote token validation, conveniently skipping the token introspection extension that _every other IdP supports._

People writing OAuth2 clients reasonably assume that trustworthy IdP's will behave normally and predictably, which means Azure AD/Graph(/Entra ID?) is probably not compatible with the libraries you're using. Fortunately Microsoft provides their own perplexing, over-engineered SDK for a handful of languages. All you have to do is rip apart your existing OAuth2 plumbing and carve another Microsoft-sized hole into your otherwise nice and standardized application.


In my case, for apps that only need to authenticate users with AzureAD, I've added the required information to the tokens and called it a day.

I wouldn't bet the farm on it, but I seem to remember that by default, the mozilla-oidc implementation for Django was expecting an userinfo-like endpoint. And I remember wasting an unbelievable amount of time trying to figure how to add information to that endpoint. I've never found anything, I just patched the lib to retrieve the relevant fields from the tokens.


The articles say these sites were validating the tokens - as in checking signatures - so they were doing that properly.

The issuer is another matter. Facebook or any other social provider is different than using AD. With AD, every customer would have a separate and distinct issuer related to their specific org and config. For social auth, there would be ONE issuer that everyone shares.


The original article [1] explains it much further:

> As you read previously, according to the Facebook documentation, when Vidio.com receives the access token from the user, Vidio should verify that the access token was generated to its App ID (92356) by calling the https://graph.facebook.com/debug_token API.

Confirming what vladvasiliu said.

1. https://salt.security/blog/oh-auth-abusing-oauth-to-take-ove...


Gitea serves as an OIDC provider:

* https://docs.gitea.com/next/development/oauth2-provider

However, I don't see an equivalent API in gitea to the "debug_token" api.

If I'm developing an application that allows "Login with Gitea", how do I make sure my application is not vulnerable?


See caseysoftware's reply: https://news.ycombinator.com/item?id=38022536

The access token usually has an `aud` field that says for whom it is.

I'm not familiar with Gitea's implementation, but reading your link, it would seem that it acts as an oauth2 provider so that 3rd parties can access Gitea, not some other random app.

> Gitea supports acting as an OAuth2 provider to allow third party applications to access its resources with the user's consent.


IIRC, the user woud get an access token from Facebook for the target site, e.g. Vidio. Vidio then has to make sure that not only is the token actually signed by Facebook, but also that Vidio is the token's audience. The token shouldn't be opaque to Vidio.

I'm in no way an authority on OAuth, but I think the "opaque token" you're talking about is in the situation when Vidio would want to access Facebook on the user's behalf: they would get an access token for Facebook. The token can be opaque to Vidio.


Not being a full expert on it either, that flow is roughly right. Vidio does have to validate it. The entire pkce flow is bloody painful to implement, and it's actually quite hard to do it perfectly, and easy to accidentally skip a part of it and have it seemingly work ok.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: