76

Which is the correct way(best practice) of adding secret API keys in flutter in case I want to push the code on github. I've made a simple app that consumes an API but I used the key in a crud way just to test whether the app is working. Usually from my experience developing applications in the back-end, Keys are stored somewhere and in different file then one would simply import it to the required file that needs the API_KEY and exclude the file in .gitignore file.

So far I have also implemented this approach:

Folder tree

-lib
  -auth
    -keys.dart
    -secrets.json 

secrets.json

This is where I will add the KEY and specify this file in .gitignore to be excluded from being added in github when I push my code.

//Add API KEY HERE
{
  "api_key": "ee4444444a095fc613c5189b2"
}

keys.dart

import 'dart:async' show Future;
import 'dart:convert' show json;
import 'package:flutter/services.dart' show rootBundle;


class Secret {
  final String apikey;

  Secret({this.apikey=""});

  factory Secret.fromJson(Map<String, dynamic>jsonMap){
    return new Secret(apikey:jsonMap["api_key"]);
  }
}


class SecretLoader {
  final String secretPath;

  SecretLoader({this.secretPath});
  Future<Secret> load() {
    return rootBundle.loadStructuredData<Secret>(this.secretPath,
            (jsonStr) async {
          final secret = Secret.fromJson(json.decode(jsonStr));
          return secret;

        });
  }
}

I feel like this approach is too much. I would like to get suggestions of a better approach.

Philip Mutua
  • 6,016
  • 12
  • 41
  • 84
  • 7
    If this shouldn't be shared; ideally it shouldn't be on the client. Ultimately once you ship your app, peoples can just decompile it to get everything within. – Rémi Rousselet Aug 17 '18 at 12:21
  • 1
    I'm using an API that requires me to add the key so that I get the payload I need. The app is consuming the API and add a key is required to access the payload. Where can I store the keys so that I access them when needed? – Philip Mutua Aug 17 '18 at 12:32
  • I like this method of doing things. I've done very similar things in my apps. I will also make a `main_dev.dart` and `main_prod.dart` -- each file will be targeted for running that version of the app. In each file, i will import the corresponding `secrets.json` and use it throughout the app. – MaylorTaylor Jul 21 '19 at 04:20
  • how are you actually retrieving the api key string in your code? – fiixed Nov 16 '19 at 20:59
  • 5
    if you rely on gitignored file with secrets, just make a dart file with `static const` keys. Much easier to use – zgorawski Jun 22 '20 at 06:07
  • **@RémiRousselet your comment is misleading and INCORRECT!** API Keys **MUST be hard coded!** That's how you connect your app to a Firebase project and any other cloud services. If you want to prevent reverse engineering, use obfuscation. For more information and links to official resources, please check out my answer https://stackoverflow.com/a/73455722/12695188 – genericUser Sep 06 '22 at 08:41

7 Answers7

15

API Keys must be hard coded.

Why?

Because it's impossible to retrieve the key from an external source without having a key to that source, and you can't store it securely at the OS level without having the key within the app.

Example

When you connect your app to a Firebase project or Google Cloud servers, you basically authenticate using a hard-coded API key, that you have downloaded into your app when initiating your cloud project (read more).

Security

There are two essential steps to secure your critical assets:

  1. Keep your secret keys out of version control.
  2. Use obfuscation to make it difficult for attackers to reverse engineer your application, and reveal your API Key.

IMO, those are the only steps you can take to secure your app API Key.

genericUser
  • 4,417
  • 1
  • 28
  • 73
10

EDIT: Look at Sludge's comment below.

EDIT 2: The issue in described at the bottom has been fixed in firebase-config 19.0.2.

Use Firebase Remote Config. Inside the Firebase console, inside the menu, scroll down to Grow and then Remote Config. Here you can add a parameter with a value. When you're done don't forget to publish the changes. It's kind of subtle.

enter image description here

Now install firebase_remote_config for Flutter.

After importing everything, you can retrieve your value using this code:

RemoteConfig remoteConfig = await RemoteConfig.instance;
await remoteConfig.fetch(expiration: Duration(hours: 1));
await remoteConfig.activateFetched();

remoteConfig.getValue('key').asString();

This way, the API key or token is never part of your application.

Note: there is currently an issue where you get a warning stating the application's name is not set, but this won't affect functionality.

Noodles
  • 3,888
  • 2
  • 20
  • 31
  • 5
    I like this strategy. However, then you have documentation like https://pub.dev/packages/google_maps_flutter#getting-started that tells people to place the API KEY in `AndroidManifest.xml` or `AppDelegate.m`. Is this not bad practice? How should one replace this strategy? – AWhitford Feb 22 '20 at 16:40
  • 43
    Per Firebase: "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." (https://firebase.google.com/docs/remote-config) – Sludge May 21 '20 at 12:31
  • Any suggestion on how this can done. Do you think that firebase realtime/datastore is a better choice? – user2570135 Jul 07 '21 at 21:08
  • 3
    **This is a bad answer**, since in order to connect to Firebase project, you have to download a hard coded API_Key first. In other words, this approach is meaningless and misleading. https://firebase.google.com/docs/projects/api-keys#find-api-keys – genericUser Sep 05 '22 at 11:58
  • @genericUser do you have a better solution? – Jsoon Sep 06 '22 at 04:39
  • 1
    Of course! :) Check out my answer https://stackoverflow.com/a/73455722/12695188 – genericUser Sep 06 '22 at 07:10
  • Firebase Remote Config documentation (https://firebase.google.com/docs/remote-config#policies_and_limits) we can read the following: 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 this answer should be marked as not valid – egonzal May 24 '23 at 10:06
  • Dunno! Since I've started work in mobile apps using Firebase solutions in 2018, I've seen how it's easy to attackers to get information when a device calls Remote Config. Besides each device waits 4 hours to recall Remote Config (except when you reinstall the app in your device), but this is not the default average user case. A regular user in production updates his/her app from Play Store/App Store instead of uninstall then download from stores. It it was a good way to do that remotely, it would be in Realtime Database, which is "real time" and it's not charged per requests. – Fellipe Tavares Jun 21 '23 at 14:46
9

For secure storage you have to rely on the corresponding native platforms, both iOs and Android provide a mechanism to securely store keys. You can implement it by yourself and use the flutter channels to obtain and store the keys. Information about this mechanism can be read here:

Android Keystore

iOs KeyChain

Also, you can use this flutter plugin, which uses the services mentioned above and provides a dart object to access the secure storage.

Rolando Urquiza
  • 1,404
  • 3
  • 17
  • 29
4

As mentioned, if the key is a secrete and you would like to protect it then simply do not put it in the client app. The app can be de-compiled and the key can be extracted for person willing to target your client.

I would delegate the task of communicating with this API to your Application Server. You can put the key in your server and have your server communicate with this external API and relay the response to the client.

Edit: Another approach, which is less secure but more convenient is to obfuscate your code using something like proguard. See this page for flutter instruction on android app: https://flutter.io/android-release/

Gainz
  • 1,639
  • 2
  • 8
  • 10
  • 2
    I do not want to invest extra time building a back-end server for this, I did my research and found out how android handles this when building it with java :(https://stackoverflow.com/questions/14570989/best-practice-for-storing-and-protecting-private-api-keys-in-applications). There must be a way for flutter to handles this. Also here https://github.com/codepath/android_guides/wiki/Storing-Secret-Keys-in-Android – Philip Mutua Aug 17 '18 at 14:17
  • 18
    But if you request the API from your own server everyone will be able to do that if they know the URI that they could again retrieve from your code. – Noodles Aug 06 '19 at 15:03
  • **This is a bad answer.** There is not other option then saving the API Key hard-coded on the client side. That's the hole idea behind API keys. – genericUser Sep 05 '22 at 12:00
0

The best practice should be to create a Firebase function, and then have that function use the secrets storage. This approach keeps the secrets out of your source code so there are no issues with sharing the code, and stores them securely. Firebase authenticates your app (and users if you use the login feature).

Secret Parameters

Call functions from your app

0

A way of storing API Keys safely in flutter local repository from not being uploaded to a remote repository, is to use dart package flutter_dotenv link to package

osw
  • 1
  • 1
  • 2
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 28 '23 at 01:29
-4

You can use flutter_secure_storage from the oficial Flutter Packages

Álvaro Agüero
  • 4,494
  • 1
  • 42
  • 39
  • 15
    Possibly a stupid question, but when using this package, I will still rely on putting the keys in my source code, since they eventually have to be added to the secure storage at some point, so I can read them later. Or am I misunderstanding something here? – Thomas Jun 13 '19 at 11:44
  • 12
    I think you understand it correctly. That package helps if you get secrets from your *user* and want to store them safely. – harm Jul 17 '19 at 08:53