visit
Mobile apps commonly use APIs to interact with back end services and information. In 2016, time spent in , reinforcing most companies mobile-first strategies, while also providing fresh and attractive . As an API provider, protecting your business assets against information scraping, malicious activity, and denial of service attacks is critical in maintaining a reputable brand and maximizing profits.
Properly used, API keys and tokens play an important role in application security, efficiency, and usage tracking. Though simple in concept, API keys and tokens have a fair number of gotchas to watch out for. In Part 1, we’ll start off with a very simple example of API key usage and iteratively enhance its API protection. In Part 2, we will move from keys to JWT tokens within several OAuth2 scenarios, and in our final implementation, we will remove any user credentials and static secrets stored within the client and, even if a token is somehow compromised, we can minimize exposure to a single API call.
In the sequence diagram, the client is a mobile application. The resource owner is the application user, and a resource server is a backend server interacting with the client through API calls. We will use terminology as much as possible. With each API call, the client passes the API key within the HTTP request. It is generally preferred to send the API key as part of the authorization header, for example: authorization: key some-client-id URLs are often logged, so if the API key is passed as a query parameter, it could show up in client logs and be easily observed, as demonstrated by this past . This initial API key approach offers some basic protection. Any application making an API call will be rejected if the call does not contain a recognized ID. Different applications with different keys could also have different permission scopes associated with those keys; for example, one app could have read-only access while another may be granted administrative access to the same back end services. Keys can be used to gather basic statistics about API usage such as call counting or traffic sourcing, perhaps rejecting calls from non-app user agents. Importantly, most API services use calling statistics to enforce rate limits per application to provide different tiers of service or reject suspiciously high frequency calling patterns. One obvious weakness with this simple approach is that the API call and key are passed in the clear. A could successfully modify any API call or reverse engineer the API and use the observed API key to make its own malicious API calls. The compromised API key cannot be blacklisted without breaking existing application instances and requiring an upgrade of the entire installed base.
Though the secret is known by both client and server, that secret is never present in the communication channel. An attacker might somehow see the ID, but without the secret, he cannot properly sign the request. As it stands, an attacker can still deny or replay the request, but he cannot alter it. Examples built around this scheme include the HTTP authentication specification or the . To further protect critical information from being observed, all or portions of a message can be encrypted before signing using key material derived from the shared secret.
As we saw for application keys, we can use user keys to gather statistics and set authorization levels, but now we can do it with user-level granularity. Assuming we are using both app and user keys, the authorization levels for a user will be a function of both app and user; for example, a user may have administrative authorizations on one app while having only read permission on a different app, even though they are talking to the same backend server. Similar to when using , session state is typically maintained on the server. This may decrease server scalability, and if multiple servers can handle a user request, session data must be synchronized between them. We’ll address this with user tokens in part 2. So far, our application keys are static and therefore have infinite lifetimes. By contrast, user keys are created on the server, and they can and should expire. When a user key expires, the user must reauthenticate to continue making API calls, and session state is lost. Users do not like to logon repeatedly, so a policy decision needs to be made on key lifetimes. The longer the lifetime, the more user convenience, but if a user key should be compromised, it could be used maliciously for a longer time as well. If a key can last longer than an application instance, then it must be stored in persistent storage on the client between app invocations. This is inherently less secure than if the key only exists in memory. Use secure storage such as for IOS and consider for Android. Unlike an application key, a user key can be revoked without breaking installed applications.
We started off with a very simple example of API Key usage and iteratively enhanced its API protection to secure the communication channel and authorize both clients and users using API keys. In Part 2, we will move from keys to JWT tokens within several OAuth2 scenarios, and in our final implementation, we will remove any user credentials and static secrets stored within the client and, even if a token is somehow compromised, we can minimize exposure to a single API call.
Thanks for reading! I’d really appreciate it if you recommend this post (by clicking the ❤ button) so other people can find it.Originally published at .