How to store API keys in Flutter (July 2020)

Viewed 2811

It's July 2020.

The pending question is: If my flutter app requires secret string to operate, say an API-key, then what strategy would prevent a malicious party from being able to extract this API-key from the published app?

I found some discussion threads on this subject, but none seem to arrive at an answer.

Use flutter_secure_storage

I don't think that works. I can use this to pass user input to Keychain/Keystore where it will remain forever safe. But unless I personally type the API key into each installation of my app, this can't be used for storing an API-key... without the API-key being in the code/assets

Store the API key on a Backend server, only pass results to app

Well that just creates the same problem with extra steps. My Vendor API-key is now safe on my backend server. But what ever 'key' I am using to police data requests from my backend server ... well that needs to be in the flutter code/assets

Use a firebase remote config

firebase says don't do that:

Don't store confidential data in Remote Config parameter keys or parameter values. It is possible to decode any parameter keys or values stored in the Remote Config settings for your project.

Ok, so that's a couple of ways that I think do not work. Is there actually a strategy that does work?

4 Answers

I have some solutions for it.

  1. Make it harder to understand -

Split your API key into many Strings..store them at random places and combine them while making the call, in a very complex manner..in short, make your code complex to understand.

  1. Store your API key in Firestore -

Store your API key in a document, and retrieve it, when making the API call.

(..I think this is a better way)

Lemme know what you think. :)

Replying to above.

Personally I do not know how effective de-compiling an .apk can be. But there are millions of people out there, and some might be very good at this. Personally I am assuming 'worst case' that someone could get a hold of my flutter code and run it in Android Studio.

If they can do that, not just 'look' at decompiled code, but actually run it, then they can just use print() anywhere to learn secret strings like api keys.

My current Solution: My project involves user accounts. So I am combining 2 ideas from my original post.

  1. The api key lives on a backend server
  2. the key to getting data from the backend server are the credentials entered by the user. This way a 'bad guy' running my flutter code is limited, best they can do is create an account and use the app. I can always limit the number of calls any one user can make, to limit abuse, as I would on a website.

As others have mentioned, you can store your API keys in Cloud Functions and then make all of your HTTP requests from these deployed functions.

You can also restrict usage of these functions by creating them as Callable Functions and then checking if the caller of the function is authenticated with your Firebase project.

Whenever the function is called, check the auth status of the caller by referencing the context parameter.

 if(!context.auth){
      throw new functions.https.HttpsError('unauthenticated','This function must be called while authenticated');
    }

So in summary:

  1. Create a callable cloud function that uses your API key
  2. Deploy the function
  3. When app users call the function, check their auth status
  4. If the user is authenticated, make your HTTP request using your API key

Restricting API key to authorised users is not a great security benefit assuming it's easy to register a new one and you are facing all mighty intruder who managed to reverse-engineer your whole code and API key is constant between all users.

Also, I believe, that if somebody has managed to reverse-engineer your whole application it should be possible to do a memory dump of the application at the moment we decrypt (which is unavoidable) our API key in order to use it and steal it.

Best way to theoretically harden intruder's job is to:

  1. Generate API key for each user/device/etc at the backend side so you can control misuse. Add limits to API key usage: both lifetime and rate wise. Add some other heuristics on top of the API keys usage data.
  2. Use some kind of https://firebase.google.com/docs/app-check so your backend can ensure (at some point of certainty, of course) that the user requesting the key is your authentic app.
  3. Store unencrypted API key in app's/device's memory as short time as possible.
  4. Try to reverse-engineer the app yourself or request an audit and check the ways to further harden reverse-engineering your application to misuse API key. I believe that this would be some kind low-level tricks and hacks to harden reverse-engineering like code obfuscation.

I'm not a security engineer and would be happy to see additional info about how difficult is the task of stealing API key in theory for the above case of an Android/iOS Flutter app where we implemented everything beside the last point.

Related