Preface
OAuth is a fantastic login protocol that allows disparate web applications to all use the same login provider. However, I’ve discovered that its use in native mobile applications is borderline dangerous, and in this post I’ll describe why.
Last year I released my first (and still only) iPhone application. Even though my app doesn’t use OAuth, the then recent Twitter/OAuth fiasco stoked my interested in mobile OAuth. I was particularly curious how OAuth providers protected their passwords in their mobile SDKs.
I downloaded the Facebook SDK and poked around in the iPhone simulator, and frankly I didn’t like what I saw. In less than an afternoon, I was able to build an app that could steal a user’s password as they logged in through Facebook Connect, all 100% transparent to the user, 100% transparent to Facebook, and 100% transparent to Apple. In a word, this is not a good sign for OAuth and native mobile apps. To be clear, this affects every single OAuth provider and mobile platform, not just Facebook and iOS.
Despite numerous web searches, emails, and even code submission to Facebook, I haven’t seen anyone talk about this problem on the web at all. Seeing as how the Facebook Connect SDK is almost 2 years old, and many other companies are following suit with OAuth in native mobile SDKs, I think this conversation is long overdue.
In this post, I’m going to explain how and why OAuth in native mobile apps does not secure your password, and why you should still be wary when logging into Facebook/Twitter/Google/anything through any app except their respective official native mobile apps. I’m writing this post to expose the still weak security for single sign on services in native mobile apps. I am not providing any source code, though after reading this post I’m sure you’ll agree that eavesdropping on an OAuth transaction in a native mobile app is extremely easy even for a novice programmer.
The Problem
The purpose of Oauth is to let application developers use Twitter/Facebook/anysite’s login mechanism, all while keeping the password 100% secret from the application developer. In this world, if the application turns out to be an untrusted malicious application, all the user needs to do is turn off access from that application on Twitter/Facebook/anysite’s settings page.
However, native mobile application developers are still able to access (steal) your password even when using OAuth for login, all 100% undetected. This means that for native mobile apps, using OAuth is not any more secure than using Basic Auth, and Twitter’s API change from Basic Auth to OAuth does nothing to protect user’s passwords from malicious application developers.
To be clear, this affects every OAuth implementation in native mobile apps, across iOS, Android, and [I believe] Windows 7.
When your password is safe: Web Apps
As our first example, let’s take a look at web apps – this is true for both mobile web apps and desktop web apps.
OAuth succeeds in protecting the user’s login information because the web browser is a 100% trusted agent and ensures that the application code on the web app’s domain name cannot access any information on the OAuth providers domain name. This is called Same Origin Policy.
For example, when [insert malicious web app here] begins an OAuth login, the browser opens a window/redirects/iframes to the authentication provider (probably Facebook, Twitter, Google, etc). Due to the browser’s security, it is impossible for the malicious app to eavesdrop as the user types in their password. After the user enters their username + password into the legitimate login screen, approves (or disapproves) the web app from accessing it’s account, and is then redirected back to the potentially malicious web app.
At no point in this transaction does the malicious web app have access to the user’s login credentials. Browser security prevents any scripts on the malicious site from interfering/listening to/intercepting the login credentials. It’s this trusted and neutral 3rd party browser that makes this negotiation possible. The phone’s built in browser acts as the trusted middleman during the OAuth transaction.
Once the user discovers the malicious apps evil plot, all the user needs to do is login to the OAuth provider’s trusted site and remove the malicious app from their trusted apps. done.
You’re safe, the world is happy, and OAuth saves the day. Not so in native mobile apps, let’s take a look:
When your password is not safe: Native Mobile Apps
In native mobile apps, the mobile application itself takes the place of the browser, so there is no longer a trusted neutral 3rd party browser to act as the trusted middle man in OAuth transactions – the mobile app itself acts as both the OAuth client and the OAuth middle man!
So what does this mean? It means any native mobile app implementing OAuth can steal your login credentials, completely transparent to you the user, the OAuth provider, and even the phone manufacturer/carrier.
Since the mobile app hosts a tiny instance of the web browser inside of itself, that app can see/do/act on anything inside the web content it shows – no exceptions. This means the Same Origin Policy we discussed above is now out the window. Even though the native app uses WebKit to do the rendering/browsing, that native app can also do absolutely anything to any webpage loaded inside of it, including stealing passwords, without the loaded website even knowing.
I’m not going to show any code, but I’ve verified in code that this is in fact possible. I’ve effectively stolen my own Facebook password using Facebook’s provided iOS OAuth library. It only took minor modifications using entirely publicly available APIs in iOS. The exact same attack could be used in any app that logins with Twitter, and is not specific to iOS – this affects Android (and presumably Windows 7 though I haven’t looked at their API).
How the attack works:
- Malicious attacker builds native mobile app and uses any publicly available OAuth SDK from Facebook/Twitter/anyone
- The attacker loads the legitimate OAuth login screen using a web view available from the phone’s public SDK
- The attacker runs JavaScript on that webview object, attaching JavaScript event handlers to any <input> element on the page
- When the value of an <input> changes, the injected JavaScript notifies the wrapping native application about the change
- User authenticates, and OAuth transaction is completed as normal – except the malicious app has been listening to every keystroke! Password: compromised!
- Malicious app uploads credentials to anonymous server in China
Since the attacker is (a) using publicly available APIs to (b) inject JavaScript on the authentication page (even if the page is over SSL), the malicious app developer is able to listen as the user types in their credentials. The OAuth provider has zero visibility that this attack is going on; there is no way to mitigate it from the provider side. Even a walled garden app store wouldn’t be able to weed out malicious apps based on (a) static analysis of code or (b) watching for malicious network traffic.
This is all because the embedded browser in native applications cannot be the trusted middleman in the OAuth handshake.
Mobile Web: 1, Mobile Native: 0
Currently, the only secure way for OAuth to exist in mobile is for mobile web applications. These applications still run in a trusted 3rd party browser that prevents cross domain script execution. Since mobile native apps fill the role of OAuth client and trusted browser, the user’s login credentials are not secure.
It should be noted that OAuth’s website makes no mention of using OAuth for native mobile apps, but clearly mobile app developers are still using it to solve the elusive secure authentication problem.
Again, this affects 100% of OAuth implementations in native mobile applications, regardless of OAuth provider or mobile platform.
Solutions?
For a long time, Twitter provided only basic auth for it’s API, but its more recent OAuth API isn’t actually any more secure. I believe that the only way to allow secure OAuth within native mobile apps is to have a more strict web browser API for native app developers, and to properly notify OAuth providers when their service is being accessed from an insecure mobile browser. Unfortunately the safest solution right now: don’t use OAuth.
I’m not sure what the correct long term solution is for login in native mobile apps, but OAuth currently falls very short. Mobile still has a long way to go to keep users passwords and data safe.