Today's open standards ensure that when a user chooses to login, the user’s authentication is protected and only delivered to the mobile app that initiated the authentication. However, how does the Authorization Server identify or verify the invoking app? This talk will look at the potential for mobile app impersonation and mechanisms available to protect against these attacks.
- What’s needed to impersonate a mobile app
- How standards currently address this exposure
- Mechanisms to strongly identify a mobile app
All right. So you have a mobile app. You're interacting with it either as a user or as the identity provider behind the mobile app, which one is bad. And how do you know? Right. So to sort of figure this out, the first thing we're gonna go over is just a quick review of how does mobile app authorization work and Victoria's smiling in the audience because these are the identical cons offered by O zero that I've been putting in my decks recently. So there you go. I may misuse them. So, you know, I can get corrected later. So basically you have a mobile app, the, the two little boxes down there in the bottom left to basically represent the mobile app and the web browser, right? Because in normal mobile app authorizations, you do the authentication authorization part through the browser. So the user says they wanna sign into the mobile app, the mobile app sort of invokes the, the web web kit or the standalone system browser.
You'll see about that in a minute, the user gets redirected over to the company IDP, right? The IDP asks the user to log in. They get redirected back with a code. This is sort of all standard open ID, connect, OAuth, two stuff, right? And then the application right. Gets invoked via some mechanism. And we'll talk about those mechanisms in a minute and then basically exchanges the code for tokens, right? And in this case, I threw the refresh token and the identity token. So consider this an open ID connect flow, as opposed to an OAuth flow. However, there's that sort of thing there with the red X and that thing with the red X represents the fact that the mobile app is not doing any sort of client authentication, right? In a normal OAuth open ID connect flow between web servers. That step would include a client ID and a client secret, or some other form of client authentication.
That's stronger as part of that step. And in that context, the authorization server, the open ID provider knows which client it's talking to because there is a client authentication piece to that token exchange, right. Code for tokens. So let's talk a little bit about what are in the standards today. So what, you know, go all the way back to the beginning, right? Oop, 67 49, right? It basically creates this distinction between two kinds of clients. You have a public client and you have a confidential client. The con the sort of core definition here is that the confidential client can protect the secret, which in this case was just a shared secret in the base of us 67, 49. And those clients who can't protect a secret, a webpage like a single page app or a mobile client, because you can't embed the secret into the client binary because anybody can reverse engineer the binary and extract the secret.
So mobile apps are inherently public clients, which means not client authentication. So we ran into a bunch of different attacks because of this. One of the first ones had to do with the fact that most uses of getting the browser to reinvoke the mobile app at the end of the flow to pass the code back to the mobile app, used custom schemes and on all the OS platforms, when you have a custom scheme it's arbitrary, or the OS asks you, which app do you want to reinvoke? So to protect against that proof key for code exchange was created. And really the point of this specification is to ensure that only the app that starts the authentication authorization flow can actually get the tokens out of it. So even if someone else gets the code, it doesn't help the many because they can't exchange that code and get the tokens it's done with effectively.
An ephemeral key that gets created. The purpose of this talk is to not go through that spec. There is a link to the RFC. The key element here though, is that the app itself is not authenticated in any way. You're just ensuring that the one, whatever app started the process is the only app that can complete it. We also, oh, wow. My bullets didn't come through that. That's okay. There's also an RFC for, you know, best practices for native apps and in best practices for native apps, it basically says, right. There's multiple ways that the, the browser can, reinvoke the mobile app, you've got custom schemes, you've got claimed URLs, and you've got, you know, a local host URL, which means effectively the, the client is running a little mini web server, and you're sort of redirecting it back into a web server running on the client.
I haven't seen very much of that in the wild, but it's in the spec. So the recommendation is claimed URLs. So let's talk about claimed URLs for a bit. So the concept of claimed URLs is that basically the app can say this HTD PS, hopefully a URL is, is, is the mechanism that invokes my app. And this is how, like, when you are navigating the web and you click a link and it automatically invokes the app associated with that, like a Google maps link, and you have Google maps installed on your, on your mobile app and you click it. It just automatically opens Google maps. It's doing that by, based on this claimed URL. So if that Google maps isn't installed on the app, it will go to that HTTPS URL. If the app is installed, it'll invoke the app, right? So, so both iOS and Android support this.
The, so it's basically trying to create this binding between an HTTPS URL and the mobile app. So that's sort of the goal. The problem that you sort of run into is that on Android, this binding can be marked optional or required, but even in the context of required. And I apologize for the people who just walked in, we did start early cuz the, but hopefully you can catch up the, the crux of the matter is on Android. These, these links, even if you require them to be verified, if the app is not installed on the mobile device, anybody else can also still claim that URL.
And so because of that, you can't, there are mechanisms. So this is sort of what claimed URLs are. And, and it is the recommendation though, the best practices for NA apps basically says, you need to support all of them. So basically we have, we've got all these other best practices. Where are we at mobile apps? We have their, their client IDs themselves have never, ever been public in any, I mean, men private are, are secured in any way in any of the specifications. So that's always a known value. The apps themselves, can't, there's no client authentication method for the apps. And even if you're for callback URLs, even if you're using claimed URLs, you still have problems on Android. So sort of what does this mean from a threat perspective?
Well, you have a sort of a direct impersonation attack, right? Where I see your, your public, your client ID, I see your custom scheme URL. I put them in my app. I, I ask the user to authenticate. They end up at your site, you know, it, it, the other person's IDP, right? As the malicious app, it's going to the thing authorizing with whatever scopes I'm asking. And, and then it, it calls back into my app, right? Because I may be the only app installed on this device. That's claiming that custom scheme and I'm in, right. And I now have all the privileges of whatever that other app was. And from the, from the IDP or the authorization service perspective, it has no way to know whether the app talking to it is the correct app or not. And I'm sure that if for those of you who have sort of dealt with this or have apps that are dealing with high value resources, you come up with your own internal mechanisms to try and prevent this kind of a thing.
So this little table sort of gives you a view of the, the attack parameters and whether it works or not. Right. So client ID and custom scheme is basically broken on all platforms, client ID and claimed URL works on iOS. Apple did a better job at creating that binding and enforcing it regardless. It's partially works on Android. If your, if the real app is installed on the Android device and you've required that the HTPs claimed URL is verified, must be verified, then it will work. If your app is not installed on the device, then it's still attackable. And then of course the, again, I haven't seen too many people. All right, I'll ask a question. Does anybody have a mobile app that uses local host as the callback mechanism? All right. That's what I figured. So it's there. I don't think most people to use it.
You also have a little bit of a sort of silent impersonation or, or a privileged de escalation kind of an attack that can happen. And to sort of set this up. I have to describe that in mobile apps, we have a concept that I don't think is really codified in any standard, anywhere between a first party app and a third party app. The first party app is basically an app that I, the, the company wrote and it's my app, and I've got it deployed. And a third party app is sort of my developer, you know, people using my developer program to access my APIs and my resources. So I'll give you a use case with a dead app. So I used to work at AOL for over 25 years, and we had an instant messaging app called aim, right? So for many of you, hopefully you downloaded aim and have used it in the past.
If you downloaded aim to your mobile app and you went to log in and the app said, thank you for your credentials. Now we need your consent. Do you agree to allow aim to read your buddy list and send messages? Right. That feels really weird, right? The user implicitly gave consent when they downloaded the app. So why am I asking for consent again? So in the first party context, right, many companies turn off that consent bit. All right. And the third party case, it makes sense, right? Some third party integrator wants to you to log in so that they can, you know, send messages and see your buddy list. You, you want the user to explicitly give consent in that context. So you have these two con scenarios. So now consider that I go to the developer program and I sign up for a key.
And I put that in my app and I ask the user, Hey, sign in with acme.com, right? And the user goes through that and they see the consent screen. And the consent screen says, oh, this app doesn't want anything. Right. You're and the person says, great, good. Then the app starts another flow, right? Impersonating Acme dot coms, client ID, right. Using their custom scheme. And now, because they did this in the standalone browser and the users already logged into Acme, right? And now this client ID says, it's a first party client, not a third party client. The consent is skipped and voila, right? The attacker, unbeknownst to the user has access to potentially a whole bunch of elevated scopes. So, you know, again, claimed URLs might help in some of this case, custom schemes, if both the good real app and the malicious app are installed on the device, or are I believe in iOS, it's arbitrary, which one gets woken up and on Android, they're asking you these days. So the user might have a little bit of a chance of connect, you know, of not on that second a loop, you know, not being exposed, but it it's a real threat.
Maybe it's a good thing. We started early. Alright, so, so what's, there are, there are a bunch of solutions and I have a couple links to other people who are working on this space, who are both in the audience, Joseph, he and Aaron perk both have links at the end of this deck. But so there are multiple ways there, and people are really looking at this, but this is really to talk about a, a method, right? So we wanna combine something called dynamic client registration, which is, I'll talk about in a minute device at the station, which the OS providers have had for a while, which is, is my, you know, is my device, you know, rooted or jail broken, that kind of thing. And then a newer thing, at least in the iOS world is app attestation, who effectively is the entity that created this app.
So that's sort of the pairing. So what does that look like? Dynamic client registration is really about how do I convert a, a public client into a confidential client. And Justin who's also in the audience actually wrote that spec. So I will get corrected on anything I say wrong, which is perfectly fine with me. So both open ID connect and OAuth two have versions of dynamic client registration. They are compatible with each other. They are not exactly the same, but in this particular incantation of dynamic client registration, what we're going to do is we're going to have the mobile app create a public private key pair where the private key is stored in the secure enclave of the device. It's going to send the public key with the registration event, right? The software statement, there are multiple ways to do this, but one thing you can do with the software statement mechanism is basically use the software statement to represent what kind of app you're registering.
Like I'm registering the nail app, or I'm registering the instant messaging app, right? Backend validates that registration event, you know, creates effectively a client instance, a client ID that's unique to this particular instance. And we don't need a shared secret in this case, cuz we have public private key pair. I get my metadata back into the client. I'm good to go. So, so this effectively, now I have a, a way to do client authentication for this particular instance of this app device and app attestation are really OS provided services, right? So now the, you know, the code on the client asks the device, the OS level, right? Please give me an attestation, both for the status of the device, as well as the status of the app and best practice here you go to your server and you get Anot so that the server can tie all this stuff together.
Right? Most of these imple implementations at the OS level require Anot going in anyway. And then on the server side effectively, you validate that this attestation from the OS is signed by the OS, right by the right public. You know, you can go to the apple or Google and get the public key and validate the, the attestation, right? You validate that it's got your nots so that, you know, it's the request that you were expecting. And then in this attestation is what I've generally termed app bundle identification. It's not the same between Android and iOS and I'm not going into those details. But fundamentally what you can do is you can find out a bit of data that tells you either what key signed the app or what, you know, developer team in the case of iOS, you know, created this app and had it signed.
Right? So now I can look, use that data to say, is this an app that I expect to be calling me or interacting with me or not? So putting it all together, we create the public private key pair. We ask for an attestation, we add the attestation to our dynamic client registration event, dynamic client registration doesn't require that that posted data be signed. You could extend that in your implementation if you wanted to, because the public key is in there already. Anyway, you basically validate the attestation. So before you actually finish the dynamic client registration, you validate that this app is the trusted app that I'm expecting all the rest of the stuff stays the same. And now I know what app contacted me, right? What app asked for it. So what does that sort of give us? Right? We can protect which apps can do dynamic client registration.
If we need to, or we can assign different trust values. Hey, you know, we don't know who you are. So you asked for right mail, you know, read and write to email. We're not giving you that scope because you're not trusted, right. Even though you asked for it. So those kinds of things can come into play that we haven't had the ability to do before mobile apps can effectively do client authentication. They can prove their identity, the protect, private keys, protected and hardware, right? It increases our trust with the mobile app and some additional things that you get out of leveraging dynamic, client registration over some of the other mechanisms to protect against mobile app impersonation is that now you can actually do encrypted streams in both directions, right? So if, if let's say you're sending a push notification to your application and you don't want anybody in the middle to be able to read your push notification. Now you can encrypt the payload specifically for that instance of the mobile app, right. And only that mobile app can actually decrypt it and take action on it. So if you're thinking about sending, you know, OTP codes through that mechanism, right, this is a better way to protect them. All right. And I am out of time.
I, I I'm outta my 20 minutes. How's that my, my first warning, but we're basically done. And then the other key benefit that you get outta dynamic client registration, which is kind of unrelated to this whole securely identifying your mobile apps is that it gives you way if an app is misbehaving to affect just that app, because it has a unique client ID for that instance of the app today in your oof open ID connect world. If you start seeing traffic from a client ID and some of the traffic is good and some of the traffic is bad, you, there's not a whole lot. You can do other than trying to figure out, can I find all the bad traffic and block it, right. I can't effectively disable the client ID because when I do that, I'm going to break everybody. So this gives you a much finer grained ability to block instance, you know, bad instances in a more global way than sort of having to do it on a API by API basis or creating a blacklist.
I mean, you can't even create a blacklist of client IDs. So I think that's an additional benefit of client dynamic, client registration beyond, you know, just this sort of mobile app impersonation piece. So for me, key takeaways, right? If you're doing mobile apps at a minimum use claimed URLs and turn on the ver required verification mechanism in Android, that's the simplest thing you can do right now. And it will help a lot. It's not about, it's not a hundred percent solution, but it will help a lot consider, you know, this dynamic client registration with app access station, as a mechanism for your mobile apps. It needs more complicated, both on the client code side and on the backend server side. So you have to figure out how do I manage all these public keys? Do I want it to be stateless? Do I need to have a backend server?
You know, if I, if I make it state list, I at least need a backend cash of blacklisted, you know, client ID instance, client IDs, that kind of thing. And for sure, don't expect that the client ID showing up at your server is necessarily the app that you wrote, unless you have some other mechanism that you're using to sort of protect it. So that's sort of the, the key messages I wanted to leave you with today. And with that questions, oh, it's the wrong app, Aaron. I mean, it's the wrong version. I'll make sure that the version I have sent it in gets the link to Aaron's talk from O S w so my apologies, Erin, I did send in the, a deck with that extra link in it. So that's it.
How can we help you