Norsak
Norsak

Reputation: 181

How to store API keys in Flutter

If my flutter app requires a 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. 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 back-end 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 back-end server. But whatever 'key' I am using to police data requests from my back-end server, 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.

So, those are a couple of ways that I think do not work. Is there actually a strategy that does work?

Upvotes: 17

Views: 4744

Answers (7)

Mahendra Raj
Mahendra Raj

Reputation: 455

I'm mentioning the solution which i had used earlier in one of the Flutter apps.

  1. convert the api key to encrypted string using any one of the encryption algorithms such AES, Base64 etc.
  2. assign these encrypted values into your global Strings file or you can use flutter_dotenv if you are having separate environments such as dev,testing and prod
  3. Decrypt the encrypted values and make use of those decrypted strings which are actual api keys.

Upvotes: 0

Seyit Gokce
Seyit Gokce

Reputation: 363

A scenario for this: You will need a separate server that generates keys. Your API keys are stored on this server. Let's call this server "Key Server (KS)". The client requests the required API keys from KS when the application is first opened. KS encrypts API keys with SHA-256 or a similar algorithm. This encryption results in: Encrypted API key(s), 1 private key, 1 public key. After this stage, the key server sends the encrypted API keys and the public key that it creates uniquely for the client to the client. KS also sends the public key and private key it has created for the client to your main server. The client application sends its encrypted data and public key to your main server. The main server will give approval by comparing it with the public key it has and will deliver the encrypted data to the client application by decrypting it. You can improve this scenario, modify it and add additional layers of security. For example, setting the validity period for the private key and the public key, or associating it with the account of the logged in user.

Upvotes: 0

Marcel Dz
Marcel Dz

Reputation: 2714

I created a platform app with a complete backend and the way I implemented the communication between the client (flutter app) and server backend as well as security is the following:

  1. Clients can register and get a short term jwt to perform api calls as long as the jwt is valid. in the jwt which is not editable since its signed by my server, im providing the user id and can double check on server side if the user belongs to my app.
  2. the jwt itself is saved in secure storage at the client which is meant to be used for "critcal information", but will be destroyed if user logging out, jwt expires or another account will be logged in at the specific device. the jwt itself is short termend.
  3. if the short termed jwt expires at any time, the client automatically receives a new valid short termed jwt from a refresh token from my server, as long as the credentials provided are valid.
  4. Hold the refreshapi token and every sensible information in your backend.
  5. make sure you scope the user and its actions which can be done to your backend server.

the secure storag package is using:

  • Keychain used for iOS
  • AES encryption used for Android.

So my short term jwt is actually stored on the smartphone where you last logged in and wont be the same after short time or when you will change the device or log in in a different place.


Some more informations and personal experience

When I was deep diving into how security will work in a client to backend communication I had so many open questions and It took me really long to understand, that you can't lock your api backend to the outside. Everyone can call big companys apis if you know them just with applications like postman in the exact same way you are testing your backend code. Why im telling you this?

I invested so much time finding solutions to build a solid and secure implementation between client and backend, providing environnment variables, writing more complex code, saving critical information somewhere, put 3rd party services between the client backend communication which holds the critical information and so on. In the moment where you will save any kind of critical api or credential on the client you won't have any control. People than can do whatever they want compiling your app or any other stuff. Keep in mind: Never trust the client

You are on the wrong track If you are trying to hold the final "key/secret" at the client, posting it to your backend which needs to validate it. Forget this approach. I think many people like me at this time, who didnt yet investigate in secure applications will have the exact same thoughts.

Reading the above, the 5 steps I described earlier is a good start to bring solid security into your application. Yes it's much more work and more complex implementing it, but you can confirm, that whatever the client tries to do, the door to your backend is not unlocked by compiling the app reading some keys from your app.

Upvotes: 1

Georgii
Georgii

Reputation: 472

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.

Upvotes: 0

Code on the Rocks
Code on the Rocks

Reputation: 17596

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

Upvotes: 0

Norsak
Norsak

Reputation: 181

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.

Upvotes: 0

Madhavam Shahi
Madhavam Shahi

Reputation: 1192

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. :)

Upvotes: 0

Related Questions