~ 9 min read

How to Securely Store API Keys in Mobile Apps

Learn best practices for storing API keys on the client side.

Recently, I was developing a Flutter mobile app and I integrated the Google Maps API. When researching how to integrate it properly, nearly every blog post just hard-coded the Google Maps API key in the application. I immediately thought about the potential misuse of my API key, if a hacker would just extract my API key from the source code. I was even more shocked, when I noticed, that Google stored its Google Maps API key in the source code of its mobile applications as well! So, I dug deeper and learned how to store API keys properly and why Google’s API tutorial was still kinda correct. These are the results:

🔥Best Practices

No matter what, you should always keep these 5 guideline in mind!

  1. Don’t store your API key on client side (Except if the API allows you to configure access restrictions)
  2. Don’t expose API keys or unencrypted credentials on code repositories, even in private ones
  3. Utilize different API keys on each environment
  4. Consider using an API secret management service on server side
  5. Generate a new key if you suspect a breach

👮‍♂️Ways To Steal Your API Key

Nothing is secure on the client-side

Today, there exists a plethora of ways to extract API keys from a client-side application. Which means if you aren’t entirely sure what you are doing, don’t store your API keys on client side. To give a better understanding if your API key is secure, I have gathered the most used methods of retrieving API keys:

Access Through Your Repository

This first attack can be used against API keys in any application, no matter if it is on the client side or the server side. In this approach, the hacker inspects your commit history and searches for credentials or API keys which you might have accidentally stored in your repository at some point in time. It is the simplest attack and only requires access to the repo and a basic understanding of the used version control system (like Git). This means this attack is especially effective against every public project, like an open-source project. To avoid API key leakage, store your API key in an .env file and ignore that file in your version control system. Another solution would be to dynamically generate your API key based on information not available in your source code.

Access Through Reverse Engineering Your Application

You may think, if you compile / build your application with the API key inside, it is secure. This is not the case. The compilation of source code into byte code, like APKs, executables, or build files is just an obscurity and obscurity is not security! An example of API key retrieval through reverse engineering can be seen in static binary analysis approach.

The security of an encryption scheme must only rely on the secrecy of its key. ~ Kerkhoffs’ principle

Access Through Man-in-the-Middle Attacks

The last method is the most sophisticated, but has the ability to extract any API key stored on the client side, no matter how obscure it is stored. When your application is interacting with an API, it sends an HTTP-request to the API service. The HTTP-request has to contain your secret API key to validate your request. A hacker retrieves the API key by intercepting his own HTTP-request, which he called from your application installed on his device. This means, no matter where you store your secret API key in your application, a hacker can just track your HTTP-request with a man-in-the-middle attack and extract your API keys. If you want to read more about this topic and how to use a man-in-the-middle proxy, this article gives a good introduction: Steal That API Key with a Man in the Middle Attack

🛑Faulty Storing Methods

Storing Method of an API Key⚡ Retrievable From Your Repo⚡ Retrievable From Your APK⚡ Retrievable From MitM Attack
Store in Source Code
Store in .env File
Dynamically Generated at Runtime(✅)

In short, any API key stored on the client-side or accessible by a client can be reverse engineered!

How to Securely Access Your API

Ok, but how can I actually defend myself against such attacks? The answer depends on the different restrictions and use-cases of your API.

If the API you are using is uncritical, you don’t need to securely store your API. Examples of such uncritical APIs would be free APIs or APIs which are only relevant to your application, like a performance tracking API for your service. Such API keys can be simply stored on the client side. If you want, you can add simple protection against misuse by obscuring your source code. A simple obscuring method is the minimization of the build files of your application. This will make it harder to read the source code and to extract the hidden API key.

The API keys of critical APIs, which have usage fees or process any kind of business logic, should only be stored on the server side. The only exception to this rule are APIs that don’t handle any business logic and provide an API access restriction, which can filter out unauthenticated API requests. An example for such an exception are Google APIs. Every other API key should be kept secure by proxying the API. A proxy is a service that takes in a request and forwards it towards to a desired API. The proxy then waits for the response from the desired API and forwards that response back to the application which requested the call. Additionally, in between the forwarding, a proxy can interact with the calls and responses. If you implement a proxy to hide the API key, naturally the question arises: What if a hacker just calls your proxy API to access the API we are trying to restrict? To prevent such an attack, we can implement different strategies. The most common strategy is to implement user authentication. Here each user creates an account and if the user is logged in, the user authentication data is sent with the API call to our proxy. The proxy verifies the user and if the verification was successful, the API call is forwarded to the restricted API. The user authentication can then be monitored and revoked if the user misuses its access to the API. Generally, possible actions against API spamming are rate-limiting user request or throttling the users requests, by increasing the response time of subsequent requests. If you want to further secure your API proxy or if your services should function without user authentication you can restrict access to those APIs, by authenticating the domain, IP address, or mobile application that accesses the API proxy.

If you want to learn more about creating your how API Proxy to hide critical API keys, I can highly recommend the tutorial by Coding Garden.

Finally, you can use serverless functions to achieve similar results, or use a solution for API Proxying. For example, Google provides the App Check API, which allows you to define which applications, websites, or IPs can access specific APIs without any hosting by yourself. Not only Google provides such services, for example Microsoft for example provides the Microsoft Identity Platform to achieve a similar behavior.

✍ Google Maps API

Ok, after that digression, back to what I was trying to accomplish: Accessing Google Maps API from my mobile app without giving hackers access to my API.

The Google APIs, for example the Google Maps API, makes it quite simple. Just store and access your Google Maps API key in your mobile application and restrict access to your API key. It is counterintuitive to the above described best practice, but it works, and it’s the recommended way described by Google themselves!

  1. Open Google API Credentials Page.

  2. Select an API key you want to restrict

  3. Select a restriction option

    • For website restriction, you can provide an IP or a domain,

    • if you want to restrict access to your Android application, you need to provide a certificate fingerprint,

      # Debug linux and macOS
      keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
      # Debug windows
      keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
      # Release
      keytool -list -v -keystore your_keystore_name -alias your_alias_name
    • and if you want to restrict access to your IOS application, you need to provide your Bundle ID.

    • If you want to support multiple applications, I recommend you to create a separate key for each application to restrict downtime if your key should be compromised.

  4. Select the Restrict key radio button

This process of API restriction is applicable for every Google API

✍ Store API Keys Securely On Your Backend Like Firebase

Even as for your backend, you shouldn’t store sensitive information in any shape or form inside your project. If we already pay our money to the all mighty overlord Google for your backend hosting with Firebase, we can integrate the Secret Manager service to provide secure API storage for our cloud functions.

  1. From the root of your local project directory, run the following command:

    $ firebase functions:secrets:set SECRET_NAME
  2. Enter a value for SECRET_NAME

  3. Access your secret variables with the runWith function:

    exports.helloWorld = functions
      // Make the secret available to this function
      .runWith({ secrets: ["SECRET_NAME"] })
      .https.onRequest((request, response) => {
        // Access API key
        process.env.SECRET_NAME;
      });
  4. Deploy Cloud Functions:

    $ firebase deploy --only functions

If you don’t want to host all your data on one BaaS (Backend as a Service), you can use other proprietary solutions like AWS Secret Manager, host it yourself with Kubernetes Secret Solutions, or both with Vault.


References