The Ultimate Guide To Mobile API Security: by Randall Degges - March 23, 2015

Download as pdf or txt
Download as pdf or txt
You are on page 1of 20
At a glance
Powered by AI
The key takeaways are that HTTP Basic Authentication is not secure for mobile apps due to storing credentials on devices. JWT should be used instead along with OAuth2 flows. The API server validates the JWT by ensuring it is valid, untampered with and not expired before retrieving user details from it.

The problems with HTTP Basic Authentication for mobile apps are that the API key needs to be stored securely on the device, and sending the raw key with every request increases the chances of exploitation. JWT avoids these issues by not sending credentials with every request.

The steps an API server takes to validate a JWT are: 1) Extract the JWT from the request header or body, 2) Validate the signature using a JWT library, 3) Ensure the token is valid and not expired, 4) Retrieve the user's ID and permissions from the token, 5) Retrieve the user account from the database, 6) Ensure the requested action is allowed based on permissions.

The Ultimate Guide to Mobile API Security Page 1 of 20

The Ultimate Guide to


Mobile API Security

by Randall Degges
March
| 23, 2015
|
Mobile (https://stormpath.com/blog/category/mobile)

Update 10/23/2016: Interested in securing Android and


iOS apps? Be sure to check out our guide to Securing
Android Applications (https://stormpath.com/blog/build-
user-authentication-for-android-app) as well as our guide
to Securing iOS Applications
(https://stormpath.com/blog/build-note-taking-app-swift-
ios). And… If you want to build a REST API that your
mobile apps can talk with, we’ve also got you covered
(https://stormpath.com/blog/tutorial-build-rest-api-mobile-
apps-using-node-js).

Mobile API consumption is a topic that comes up


frequently on both Stack Overflow and the Stormpath
(https://stormpath.com/) support channel. It’s a problem
that has already been solved, but requires a lot of
prerequisite knowledge and sufficient understanding in
order to implement properly.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 2 of 20

This post will walk you through everything you need to


know to properly secure a REST API for consumption on
mobile devices, whether you’re building a mobile app
that needs to access a REST API, or writing a REST API
and planning to have developers write mobile apps that
work with your API service.

My goal is to not only explain how to properly secure


your REST API for mobile developers, but to also
explain how the entire exchange of credentials works
from start to finish, how to recover from security
breaches, and much more.

The Problem with Mobile API


Security
Before we dive into how to properly secure your REST
API for mobile developers — let’s first discuss what
makes mobile authentication different from traditional
API authentication in the first place!

The most basic form of API authentication is typically


known as HTTP Basic Authentication
(http://en.wikipedia.org/wiki/Basic_access_authentication).

The way it works is pretty simple for both the people


writing API services, and the developers that consume
them:

• A developer is given an API key (typically an ID and


Secret). This API key usually looks something like this:
3bb743bbd45d4eb8ae31e16b9f83c9ba:ffb7d6369eb84580ad2e52ca3fc06c9d

• A developer is responsible for storing this API key in a


secure place on their server, in a place that nobody else
can access it.
• The developer makes API requests to the API service by
putting the API key he was given into the HTTP

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 3 of 20

Authorization header along with the word Basic (which


is used by the API server to properly decode the
authorization credentials). Here’s how a developer might
specify his API key when authenticating to an API
service via the command line tool cURL :

The cURL tool will take the API credentials, base64


(http://en.wikipedia.org/wiki/Base64) encode them, and
create an HTTP Authorization header that looks like this:
Basic

M2JiNzQzYmJkNDVkNGViOGFlMzFlMTZiOWY4M2M5YmE6ZmZiN2Q2MzY5ZWI4NDU4MGFk

The API server will then reverse this process. When it


finds the HTTP Authorization header, it will base64
decode the result, grab the API key ID and Secret, then
validate these tokens before allowing the request to
continue being processed.

HTTP Basic Authentication is great because it’s simple.


A developer can request an API key and easily
authenticate to the API service using this key.

What makes HTTP Basic Authentication a bad option for


mobile apps is that you need to actually store the API
key securely in order for things to work. In addition to
this, HTTP Basic Authentication requires that your raw
API keys be sent over the wire for every request, thereby
increasing the chance of exploitation in the long run (the
less you use your credentials, the better).

In most cases, this is impractical as there’s no way to


safely embed your API keys into a mobile app that is
distributed to many users.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 4 of 20

For instance, if you build a mobile app with your API


keys embedded inside of it, a savvy user could reverse
engineer your app, exposing this API key, and abusing
your service.

This is why HTTP Basic Authentication is not optimal in


untrusted environments, like web browsers and mobile
applications.

NOTE: Like all authentication protocols, HTTP Basic


Authentication must be used over SSL at all times.

Which brings us to our next section…

Introducing OAuth2 for Mobile API


Security
You’ve probably heard of OAuth (https://oauth.net/)
before, and the debate about what it is and is not good
for. Let’s be clear: OAuth2 is an excellent protocol for
securing API services from untrusted devices, and it
provides a nice way to authenticate mobile users via
what is called token authentication.

Here’s how OAuth2 token authentication works from a


user perspective (OAuth2 calls this the password grant
flow):

1. A user opens up your mobile app and is prompted for


their username or email and password.
2. You send a POST request from your mobile app to your
API service with the user’s username or email and
password data included (OVER SSL!).
3. You validate the user credentials, and create an access
token for the user that expires after a certain amount of
time.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 5 of 20

4. You store this access token on the mobile device,


treating it like an API key which lets you access your API
service.
5. Once the access token expires and no longer works, you
re-prompt the user for their username or email and
passwordU.

What makes OAuth2 great for securing APIs is that it


doesn’t require you to store API keys in an unsafe
environment. Instead, it will generate access tokens that
can be stored in an untrusted environment temporarily.

This is great because even if an attacker somehow


manages to get a hold of your temporary access token, it
will expire! This reduces damage potential (we’ll cover
this in more depth in our next article (/blog/manage-
authentication-lifecycle-mobile)).

Now, when your API service generates an Oauth2


access token that your mobile app needs, of course
you’ll need to store this in your mobile app somewhere.

BUT WHERE?!

Well, there are different places this token should be


stored depending on what platform you’re developing
against. If you’re writing an Android app, for instance,
you’ll want to store all access tokens in
SharedPreferences
(https://developer.android.com/reference/android/content/SharedPreferences.htm
(here’s the API docs you need to make it work). If you’re
an iOS developer, you will want to store your access
tokens in the Keychain
(https://developer.apple.com/library/content/documentation/Security/Conceptual/k

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 6 of 20

If you still have questions, the following two


StackOverflow posts will be very useful — they explain
not only how you should store access tokens a specific
way, but why as well:

• Where should I store access tokens on Android?


(http://stackoverflow.com/questions/10161266/how-to-
securely-store-access-token-and-secret-in-android)
• Where should I store access tokens on iOS?
(http://stackoverflow.com/questions/5793128/access-
tokens-persistence-best-practices-ios)

It’s all starting to come together now, right? Great!

You should now have a high level of understanding in


regards to how OAuth2 can help you, why you should
use it, and roughly how it works.

Which brings us to the next section…

Access Tokens
Let’s talk about access tokens for a little bit. What the
heck are they, anyway? Are they randomly generated
numbers? Are they uuids
(http://en.wikipedia.org/wiki/Universally_unique_identifier)?
Are they something else? AND WHY?!

Great questions!

Here’s the short answer: an access token can technically


be anything you want:

• A random number
• A random string
• A UUID
• etc.

As long as you can:

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 7 of 20

• Issue it to a client,
• Verify that it was created by you (using a strong
signature),
• And assign it an expiration time…

You’re golden!

BUT… With that said, there are some conventions you’ll


probably want to follow.

Instead of handling all this stuff yourself, you can instead


create an access token that’s a JWT (http://self-
issued.info/docs/draft-ietf-oauth-json-web-token.html)
(JSON Web Token). It’s a relatively new specification
that allows you to generate access tokens that:

• Can be issues to clients.


• Can be verified as being created by you (more on this
later).
• Can expire automatically at a specific time.
• Can hold variable JSON information.
• Can reduce the amount of API calls to your service by
allowing users to validate / verify their API credentials
LOCALLY, without querying your service!

JWTs also look like a randomly generated string: so you


can always store them as strings when using them. This
makes them really convenient to use in place of a
traditional access token as they’re basically the same
thing, except with way more benefits.

JWTs are almost always cryptographically signed. The


way they work is like so:

• You store a secure random string somewhere on your


API server. This should ideally just be a long random
string (40 characters or so is fine).

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 8 of 20

• When you create a new JWT, you’ll pass this random


string into your JWT library to sign the token, along with
any JSON data you want to store: a user account ID, an
email address, what permissions this user has, etc.
• This token will be generated, and it’ll look something like
this: header.claims.signature — where header,
claims, and signature are just long base64
(http://en.wikipedia.org/wiki/Base64) encoded strings.
This isn’t really important to know though.
• You give this token to your user: likely an API client (eg:
your mobile app).

Now, from the mobile client, you can view whatever is


stored in the JWT. So if I have a JWT, I can easily check
to see what JSON data is inside it. Usually it’ll be
something like:

Now, this is a 100% fictional example, of course, but you


get the idea: if I have a copy of this JWT token, I can see
the JSON data above, yey!

But I can also verify that it is still valid because the JWT
spec supports expiring tokens automatically. So when
you’re using your JWT library in whatever language
you’re writing, you’ll be able to verify that the JWT you
have is valid and hasn’t yet expired (cool).

This means that if you use a JWT to access an API


service, you’ll be able to tell whether or not your API call
will work by simply validating the JWT! No API call
required!

Now, once you’ve got a valid JWT, you can also do cool

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 9 of 20

stuff with it on the server-side.

Let’s say you’ve given out a JWT to a mobile app that


contains the following data:

But let’s say some malicious program on the mobile app


is able to modify your JWT so that it says:

See how I added in the can-delete permission there?


What will happen if this modified token is sent to our API
server? Will it work? Will our server accept this modified
JWT?

NOPE!!

When your API service receives this JWT and validates


it, it’ll do a few things:

• It’ll check the token to make sure it wasn’t tampered


with. It does this by using the secret randomly generated
string that only the server knows. If the JWT was
modified at all, this check will fail, and you’ll know
someone is trying to do something nasty.
• It’ll also check the expires time of this JWT to make sure
it’s actually valid. So if you assigned a JWT a long time
ago that has long since expired, you’ll know that your
client is trying to use an old token — so you can just
reject their API request.

This is nice functionality, as it makes handling


verification / expiration / security a lot simpler.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 10 of 20

The only thing you need to keep in mind when working


with JWTs is this: you should only store stuff you
don’t mind exposing publicly.

As long as you follow the rule above, you really can’t go


wrong with using JWTs.

The two pieces of information you’ll typically store inside


of a JWT are:

• A user account’s unique ID of some sort. This way you


can look this user up from your user database when you
receive this JWT for authentication.
• A user’s permissions: what they can and can’t do. If
you’re building a simple API service where all users are
the same, this may not be necessary. But this way,
users won’t need to hit your API to figure out what they
can do all the time: instead, they can just look at their
JWT.

So, that just about sums up JWTs. Hopefully, you now


know why you should be using them as your OAuth
access tokens — they provide:

• A nice way to validate tokens.


• A nice way to pass publicly-viewable information to a
client.
• A simple interface for developers to work with.
• Built-in expiration.

Now, moving on — let’s talk about how this all works


together…

How it All Works

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 11 of 20

In this section, we’re going to get into the nitty gritty and
cover the entire flow from start to finish, with all the low-
level technical details you need to build a secure API
service that can be securely consumed from a mobile
device.

Ready? Let’s do this.

First off, here’s how things will look when we’re done.
You’ll notice each image has a little picture next to it.
That’s because I’m going to explain each step in detail
below.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 12 of 20

So, take a look at that image above, and then follow


along.

1. User Opens App


The user opens the app! Next!

2. App Asks for Credentials

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 13 of 20

Since we’re going to be using the OAuth2 password


grant type scheme to authenticate users against our API
service, your app needs to ask the user for their
username or email and password.

Almost all mobile apps ask for this nowadays, so users


are used to typing their information in.

3. User Enters their Credentials


Next, the user enters their credentials into your app.
Bam. Done. Next!

4. App Sends POST Requests to API Service


This is where the initial OAuth2 flow begins. What you’ll
be doing is essentially making a simple HTTP POST
request from your mobile app to your API service.

Here’s a command line POST request example using


cURL (https://curl.haxx.se/):

What we’re doing here is POST’ing the username or


email and password to our API service using the OAuth2
password grant type: (there are several grant types, but
this is the one we’ll be talking about here as it’s the only
relevant one when discussing building your own mobile-
accessible API).

NOTE: See how we’re sending the body of our POST


request as form content? That is, application/www-x-
form-urlencoded ? This is what the OAuth2 spec wants

=)

5. API Server Authenticates the User

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 14 of 20

What happens next is that your API service retrieves the


incoming username or email and password data and
validates the user’s credentials.

This step is very platform specific, but typically works


like so:

1. You retrieve the user account from your database by


username or email.
2. You compare the password hash from your database to
the password received from the incoming API request.
NOTE: Hopefully you store your passwords with bcrypt
(http://en.wikipedia.org/wiki/Bcrypt)!
3. If the credentials are valid (the user exists, and the
password matches), then you can move onto the next
step. If not, you’ll return an error response to the app,
letting it know that either the username or email and
password are invalid.

6. API Server Generates a JWT that the App Stores

Now that you’ve authenticated the app’s OAuth2


request, you need to generate an access token for the
app. To do this, you’ll use a JWT library to generate a
useful access token, then return it to the app.

Here’s how you’ll do it:

1. Using whatever JWT library is available for your


language, you’ll create a JWT that includes JSON data
which holds the user ID (from your database, typically),
all user permissions (if you have any), and any other
data you need the app to immediately access.
2. Once you’ve generated a JWT, you’ll return a JSON
response to the app that looks something like this:

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 15 of 20

As you can see above, our JSON response contains 3


fields. The first field access_token , is the actual
OAuth2 access token that the mobile app will be using
from this point forward in order to make authenticated
API requests.

The second field, token_type , simply tells the mobile


app what type of access token we’re providing — in this
case, we’re providing an OAuth2 Bearer token
(https://tools.ietf.org/html/rfc6750). I’ll talk about this
more later on.

Lastly, the third field provided is the expires_in field.


This is basically the number of seconds for which the
supplied access token is valid.

In the example above, what we’re saying is that we’re


giving this mobile app an access token which can be
used to access our private API for up to 1 hour — no
more. After 1 hour (3600 seconds) this access token will
expire, and any future API calls we make using that
access token will fail.

On the mobile app side of things, you’ll retrieve this


JSON response, parse out the access token that was
provided by the API server, and then store it locally in a
secure location. On Android, this means
SharedPreferences
(https://developer.android.com/reference/android/content/SharedPreferences.htm
on iOS, this means Keychain
(https://developer.apple.com/library/ios/documentation/Security/Conceptual/keych

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 16 of 20

Now that you’ve got an access token securely stored on


the mobile device, you can use it for making all
subsequent API requests to your API server.

Not bad, right?

7. App Makes Authenticated Requests to API


Server
All that’s left to do now is to make secure API requests
from your mobile app to your API service. The way you
do this is simple.

In the last step, your mobile app was given an OAuth2


access token, which it then stored locally on the device.

In order to successfully make API requests using this


token, you’ll need to create an HTTP Authorization
(http://www.w3.org/Protocols/rfc2616/rfc2616-
sec14.html#14.8) header that uses this token to identify
your user.

To do this, what you’ll do is insert your access token


along with the word Bearer into the HTTP Authorization
header. Here’s how this might look using cURL :

In the end, your Authorization header will look like this:

When your API service receives the HTTP request, what


it will do is this:

1. Inspect the HTTP Authorization header value, and see


that it starts with the word Bearer .
2. Next, it’ll grab the following string value, referring to this
as the access token.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 17 of 20

3. It’ll then validate this access token (JWT) using a JWT


library. This step ensures the token is valid, untampered
with, and not yet expired.
4. It’ll then retrieve the user’s ID and permissions out of the
token (permissions are optional, of course).
5. It’ll then retrieve the user account from the user
database.
6. Lastly, it will ensure that what the user is trying to do is
allowed, eg: the user must be allowed to do what they’re
trying to do. After this is done, the API server will simply
process the API request and return the result
normally.

Nothing anything familiar about this flow? You should!


It’s almost the exact same way HTTP Basic
Authentication
(http://en.wikipedia.org/wiki/Basic_access_authentication)
works, with one main difference in execution: the HTTP
Authorization header is slightly different ( Bearer
vs Basic ).

This is the end of the “How it All Works” section. next


article (/blog/manage-authentication-lifecycle-mobile),
we’ll talk about all the other things you need to know
about managing API authentication on mobile devices.

Simpler Solutions
As this is a high-level article meant to illustrate how to
properly write an API service that can be consumed from
mobile devices, I’m not going to get into language
specific implementation details here — however, I do
want to cover something I consider to be very important.

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 18 of 20

If you’re planning on writing your own API service like


the ones discussed in this article, you’ll want to write as
little of the actual security code as possible. While I’ve
done my best to summarize exactly what needs to be
done in each step in the process, actual implementation
details can be quite a bit more complex.

It’s usually a good idea to find a popular OAuth2 library


for your favorite programming language or framework,
and use that to help offload some of the burden of
writing this sort of thing yourself.

Lastly, if you really want to simplify things, you might


want to sign up for our service: Stormpath
(https://stormpath.com). Stormpath is an API service that
stores your user accounts securely, manages API keys,
handles OAuth2 flows, and also provides tons of
convenience methods / functions for working with user
data, doing social login, and a variety of other things.

Stormpath is also totally, 100% free to use. You can


start using it RIGHT NOW in your applications, and
BAM, things will just work. We only charge you for real
projects — feel free to deploy as many side projects as
you’d like on our platform for no cost =)

Hopefully this article has helped you figure out the best
way to handle API authentication for your mobile
devices. If you have any questions (this stuff can be
confusing), feel free to email us directly
(mailto:[email protected])!

-Randall

Comments Community 
1 Login

 Recommend 6 ⤤ Share Sort by Best

Join the discussion…

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 19 of 20

Search …

Explore the Topic

.NET (https://stormpath.com/blog/category/net)

General (https://stormpath.com/blog/category/general)

Java (https://stormpath.com/blog/category/java)

Javascript (https://stormpath.com/blog/category/javascript)

Mobile (https://stormpath.com/blog/category/mobile)

Node (https://stormpath.com/blog/category/node)

PHP (https://stormpath.com/blog/category/php)

Python (https://stormpath.com/blog/category/python)

REST API (https://stormpath.com/blog/category/rest-api)

Share a Post

   
19 125 18 85

Stormpath powers Identity infrastructure for thousands of web applications and


services. Our intuitive API, deep SDKs and expert support give Developer
Teams a complete Identity layer they can implement in minutes.

About Support

(https://stormpath.com/about) (https://support.stormpath.com/home)

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19
The Ultimate Guide to Mobile API Security Page 20 of 20

Customers Status Report

(https://stormpath.com/customers) (http://status.stormpath.com/)

Blog Resources

(https://stormpath.com/blog) (https://stormpath.com/resources)

Jobs Compliance

(https://stormpath.com/jobs) (https://stormpath.com/resources/compliance)

Press & News Security & Availability

(https://stormpath.com/press) (https://stormpath.com/resources/security-

Contact and-availability)

(https://stormpath.com/contact)

Stormpath HQ:
1825 S Grant Street, Ste 450
San Mateo, CA 94402

Sales: (Email Address) [email protected]


Support: (Email Address) [email protected]

    

CO P Y RI G HT 2017 S T O RMP A T H | A LL RI G HT S RE S E RV E D | P RI V A CY

P O LI CY (HT T P : / / S T O RMP A T H. CO M/ P RI V A CY -P O LI CY / ) | T E RMS O F S E RV I CE

(HT T P : / / S T O RMP A T H. CO M/ T E RMS -O F -S E RV I CE / )

https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security?utm_campaign... 2017/01/19

You might also like