Mobile apps are becoming increasingly important in the strategy of any company. As a result, companies need to release new application versions at a fast pace, and this puts developers under pressure with tight deadlines to complete and release new features very quickly.
Some developers may take shortcuts to achieve this delivery speed while being aware of the trade offs involved, but other less experienced developers will just fall into the trap of starting to code without doing their research first, regarding what are the best practices to develop and secure a mobile application and the API server that it communicates with.
To be able to understand why a mobile app needs an API key to identify itself to the API server, we need to be able to differentiate between WHO and WHAT is making the request to the API server and to understand what the difference is between public and private APIs.
To better understand the differences between the who and the what are accessing your mobile app, let’s use this picture:
The Intended Communication Channel represents your mobile being used as you expected, by a legit user without any malicious intentions, using an untampered version of your mobile app, and communicating directly with your API server without being man in the middle attacked.
The actual channel may represent several different scenarios, like a legit user with malicious intentions that may be using a repackaged version of your mobile app, a hacker using the genuine version of you mobile app while man in the middle attacking it to understand how the communication between the mobile app and the API server is being done in order to be able to automate attacks against your API. Many other scenarios are possible, but we will not enumerate each one here.
I hope that by now you may already have a clue why the who and the what are not the same, but if not it will become clear in a moment.
The who is the user of the mobile app that we can authenticate, authorize and identify in several ways, like using OpenID Connect or OAUTH2 flows.
Generally, OAuth provides to clients a "secure delegated access" to server resources on behalf of a resource owner. It specifies a process for resource owners to authorize third-party access to their server resources without sharing their credentials. Designed specifically to work with Hypertext Transfer Protocol (HTTP), OAuth essentially allows access tokens to be issued to third-party clients by an authorization server, with the approval of the resource owner. The third party then uses the access token to access the protected resources hosted by the resource server.
OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
While user authentication may let your API server know who is using the API, it cannot guarantee that the requests have originated from what you expect, your mobile app.
Now we need a way to identify what is calling your API server, and here things become more tricky than most developers may think. The what is the thing making the request to the API server. Is it really a genuine instance of your mobile app, or is a bot, an automated script or an attacker manually poking around your API server with a tool like Postman?
For your surprise you may end up discovering that It can be one of your legit users using a repackaged version of your mobile app or an automated script trying to gamify and take advantage of your service.
Well, to identify the what, developers tend to resort to an API key that usually they hard-code in the code of their mobile app. Some developers go the extra mile and compute the key at run-time in the mobile app, thus it becomes a runtime secret as opposed to the former approach when a static secret is embedded in the code.
Public APIs are advertised by their owners as a way to promote access to their services, and they normally include documentation in how to integrate them into your applications, and sometimes they even provide SDKs to make that easier.
Depending on the business model, some APIs may be totally open, thus not using any form of authentication, some will require a secret like an API key in order to be accessed, while others will be based on a paid subscription model that also requires a secret to access it and even may employ more advanced API security techniques in order to lock it down as much as possible from API abuse.
Now just because the documentation for your API is not public or doesn’t even exist, it is still discoverable by anyone having access to the applications that query your API.
Interested parties just need to set up a proxy between your application and the API to watch for all requests being made and their responses in order to build a profile of your API and understand how it works.
Sometimes this is made even easier when the APIs are self discoverable ones, like when their response contains links to extended and related resources. This is usually seen in APIs implementing hypermedia links with the HATEOAS standard where HAL is one of its implementations that allow for dynamically and programmatically transversing the entire API by following such links.
API response with HATEOAS example:
With all this information, it is then easy to build scripts to automate calls to the API in order to extract data or to abuse it in any way the attacker finds possible and profitable to do.
You might think your API is really private because it is just used by microservices for internal communication. Well even here APIs are not private because you can discover what subdomains exist for a domain by using the Google Transparency Report web interface or by scripting it in Python, NodeJS or by using the Fierce Domain Scanner and then crawling the domain and subdomain to find APIs on it. Sometimes the robots.txt file is a good indicator to find them, because normally we want to tell the good bots that they shouldn’t crawl certain parts of our domain, like the API, but the bad bots will not respect this and it will be the first place where they will look to see what valuable stuff is there.
So an API can be only private when both the API and client consuming it are in the same internal network and both servers are not accessible in any way from the outside world, because even if only the API client is exposed to the outside world, the API is already in danger of being indirectly abused via the client.
I hope that by now that you understand the difference between who and what is accessing your API and the difference between a public and a supposed private API, thus being aware that an API server needs a way to know what it is serving the requests to, even before it is able to know who is making those requests.
It should now be clear that every time you build a mobile app that communicates with an API server, a secret must be used to identify it, and this secret is usually named by developers as an API key.
The API key must be present in every request made to the API server and the best practices recommend to use API keys in the header of the request, not as part of the url or request body.
Links are easy to share and may end-up in the web in places like Stackoverflow when you are copy/pasting code to exemplify the API call being made.
Also, links tend to end-up in the logs with the full url, thus your API key will be hanging around in the logs as plain text and they can be easily leaked unintentionally when copy/pasting logs to some public website to get help solving an issue.
So having the API key in the url is like distributing keys for the front door of your home to anyone or losing those keys with a tag attached which contains the address of your home… yes I have seen it!
Post requests normally contain more data then just the API key, thus when sharing them in the web as an example to query the API or when looking for help, it is easy to forget to replace the real API key with a dummy one.
While this is better, headers can still be leaked in logs or when the request examples are saved into third party tools like Postman or API specs.
From all the approaches though, this one has a lower risk of being leaked, thus it should be the prefered one to be used in your mobile app.
This is a tricky one and the reply is NO and YES…
Well, it is NO because any static secret stored in the source code of a mobile app can be reverse engineered by using tools that decompile the binary of the mobile app, leaving it exposed to human eyes and tools like `grep`, but you can even try to use the `strings` tool to perform a lookup into a binary without the need to decompile it.
Let’s imagine that you are an advanced developer and went the extra mile to protect the API key and calculate it dynamically at run-time. Your effort is appreciated and will put off most of the script kiddies, but will not take away the hackers, that will use introspection frameworks like xPosed and Frida in conjunction with the already decompiled code to understand how and where the API key is generated at run-time in order to intercept and extract it. A better way exists though, using a proxy between the device that the hacker controls and the API server is a fast and easy way to grab an API key generated at run-time.
YES can be achieved by using a Mobile App Attestation solution to enable the API server to know what is sending the requests, thus enabling it to respond only to requests from a genuine mobile app.
The role of a Mobile App Attestation service is to guarantee at run-time that your mobile app was not tampered with or is not running in a rooted device. It consists of an SDK integrated in a your mobile app that runs in the background, without impacting the user experience, and communicates with a service running in the cloud to attest the integrity of the mobile app and device it is running on.
On successful attestation of the mobile app integrity, a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud are aware. In the case of failure on the mobile app attestation the JWT token is signed with a secret that the API server does not know.
The mobile app must send the JWT token in the headers of the request for very API call it makes. This allows the API server to only serve requests when it can verify the signature and expiration time in the JWT token and refuse them when it fails the verification.
Anyone trying to verify at run-time if a JWT token issued by the Mobile App Attestation is a valid or invalid one will not succeed, because the only difference between them is the secret used to sign it, and this secret is only known at anytime by the Mobile App Attestation service and the API server. This means that even the mobile app is not in possession of the secret, thus it is not aware if it is sending a valid or invalid JWT token to the API server.
Some developers may think that because they have not published their API, it is private and no one will be able to find it or that authenticating the user will be good while many develop mobile apps without any API protection at all. This means that some mobile apps are being released with a complete disregard to the most elementary of all security measures for a mobile app, the use of an identifier for when it communicates with the API server, normally called the API key.
The lesson I want to convey here is that releasing a mobile app without a way of identifying itself to the API server is like leaving your car with the doors closed but not locked, and the keys in the ignition.
On the other hand, releasing apps with API keys is like locking the door of your home but leaving the keys under the mat. Now to understand why I say that, stay tuned for my next article about extracting an API key from a mobile app with static binary analysis.