在Flutter中存储API密钥的正确方式,遵循最佳实践是什么?

78

如果我想要将代码上传到GitHub,那么在Flutter中添加秘密API密钥的正确方法(最佳做法)是什么?我已经制作了一个简单的应用程序来使用API,但我只是以crud方式使用密钥来测试应用程序是否工作。通常从后端开发应用程序的经验来看,Key被存储在某个地方,并且在不同的文件中,然后只需将其导入到需要API_KEY的必需文件中,并在.gitignore文件中排除该文件。

到目前为止,我还实施了这种方法:

文件夹树

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

secrets.json

这是我将添加 KEY 并在 .gitignore 文件中指定的位置,以便在推送代码到 GitHub 时排除此文件。

//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;

        });
  }
}

我觉得这种方法太过复杂,希望能得到更好的建议。


7
如果这个内容不应该被分享,最好就不要放在客户端。一旦你发布了你的应用程序,人们可以通过反编译来获取其中的所有内容。 - Rémi Rousselet
1
我正在使用一个需要我添加密钥才能获取所需有效载荷的API。应用程序正在消耗该API,因此需要添加密钥才能访问有效载荷。我应该将密钥存储在哪里,以便在需要时访问它们? - Philip Mutua
5
如果你依赖于被 Git 忽略的包含秘密信息的文件,那么就可以创建一个包含static const键的 Dart 文件。这样使用起来会更加容易。 - zgorawski
@RémiRousselet,你的评论是误导性和不正确的!API密钥必须硬编码!这是连接您的应用程序到Firebase项目和任何其他云服务的方法。如果您想防止反向工程,请使用混淆。有关更多信息和官方资源链接,请查看我的答案https://dev59.com/bFQK5IYBdhLWcg3wbPP7#73455722。 - genericUser
但是这对于生产应用来说不是一种疏忽吗?换句话说,如果我想为用户提供一个安全的应用程序,我总是需要构建自己的后端,这样所有由谷歌提供的Flutter设置都变得模糊不清了吗? - undefined
显示剩余2条评论
7个回答

20

API密钥必须硬编码。

为什么?

因为如果没有获取外部密钥的密钥,无法从外部源检索密钥,且在没有应用程序内部密钥的情况下不能在操作系统级别上安全存储它。

例子

当您将应用程序连接到Firebase项目或Google Cloud服务器时,您基本上使用硬编码的API密钥进行身份验证,该密钥在启动云项目时下载到应用程序中(阅读更多信息)。

安全性

保护关键资产有两个基本步骤:

  1. 不要将秘密密钥放入版本控制
  2. 使用混淆技术以使攻击者难以反向工程您的应用程序并揭示您的API密钥。

在我看来,这些是保护应用程序API密钥所能采取的唯一步骤。


这应该是正确的答案。所有其他答案都是误导和不正确的。 - genericUser
同意,最佳答案。不过,我可能会添加。 - RobbB

9

为了进行安全存储,您必须依赖相应的原生平台,iOs和Android都提供了一种安全存储密钥的机制。您可以自行实现并使用Flutter通道来获取和存储密钥。有关此机制的信息可以在以下位置阅读:

Android Keystore

iOs KeyChain

此外,您还可以使用这个 Flutter插件,它使用上述服务并提供一个Dart对象以访问安全存储。


24
这并没有帮助,因为您在某个时候仍需要将API密钥放入代码中,反编译APK仍会揭示这个密钥! - Aryeetey Solomon Aryeetey
1
同意@AryeeteySolomonAryeetey。这是一个糟糕的答案。 - genericUser

8

编辑:请看下面Sludge的评论。

编辑2:底部描述的问题已在firebase-config 19.0.2中修复。

使用Firebase远程配置在Firebase控制台内,点击菜单,向下滚动至Grow,然后选择Remote Config。在这里,您可以添加一个带有值的参数。完成后,请不要忘记发布更改。这有点微妙。

enter image description here

现在为Flutter安装firebase_remote_config
导入所有内容后,您可以使用以下代码检索您的值:
RemoteConfig remoteConfig = await RemoteConfig.instance;
await remoteConfig.fetch(expiration: Duration(hours: 1));
await remoteConfig.activateFetched();

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

这样,API密钥或令牌永远不会成为您的应用程序的一部分。

注意:目前存在一个问题,您将收到一个警告,指出应用程序的名称未设置,但这不会影响功能。


5
我喜欢这个策略。然而,像 https://pub.dev/packages/google_maps_flutter#getting-started 这样的文档告诉人们将 API KEY 放在 AndroidManifest.xmlAppDelegate.m 中。这不是一个好的做法吗?应该如何替换这个策略? - AWhitford
43
根据 Firebase 官方文档:“不要将机密数据存储在远程配置参数键或参数值中。远程配置设置中存储的任何参数键或值都可以被解码。” - Sludge
你有什么建议吗?你认为Firebase实时数据库/数据存储是更好的选择吗? - user2570135
3
这是一个糟糕的答案,因为要连接到Firebase项目,您首先必须下载硬编码的API_Key。换句话说,这种方法是没有意义和误导性的。https://firebase.google.com/docs/projects/api-keys#find-api-keys - genericUser
1
当然可以!:) 请查看我的答案 https://dev59.com/bFQK5IYBdhLWcg3wbPP7#73455722 - genericUser
显示剩余3条评论

4
如上所述,如果密钥是机密的,并且您想要保护它,则只需不将其放在客户端应用程序中。该应用程序可以被反编译,密钥可以被提取给想要攻击您的客户端的人。
我建议您将与此API通信的任务委托给您的应用程序服务器。您可以将密钥放在服务器上,并让服务器与此外部API通信并将响应传递给客户端。
编辑:另一种方法是使用类似于proguard的东西来混淆您的代码,这样做更方便但不太安全。有关flutter在android应用程序上的说明,请参见此页面:https://flutter.io/android-release/

2
我不想花费额外的时间来构建后端服务器,我进行了研究并发现在使用Java构建时,Android是如何处理这个问题的 :(https://dev59.com/AWUq5IYBdhLWcg3wV_Ii)。Flutter一定有办法解决这个问题。此外,在这里 https://github.com/codepath/android_guides/wiki/Storing-Secret-Keys-in-Android - Philip Mutua
18
如果你从自己的服务器请求API,只要其他人知道URI并可以从你的代码中再次检索,他们也能够这样做。 - Noodles
这是一个糟糕的答案。除了在客户端硬编码保存API密钥之外,没有其他选择。这就是API密钥背后的整个思想。 - genericUser

1
最佳实践应该是创建一个 Firebase 函数,然后让该函数使用秘密存储。这种方法可以使秘密不出现在您的源代码中,因此不会出现共享代码的问题,并且可以安全地存储它们。Firebase 对您的应用程序进行身份验证(如果您使用登录功能,则对用户进行身份验证)。
秘密参数
从您的应用程序调用函数

0
在Flutter本地存储库中安全地存储API密钥,避免上传到远程存储库的一种方法是使用Dart包flutter_dotenv 链接至该包

由于您目前的回答不够清晰,请[编辑]以添加更多细节,以帮助他人理解如何回答所提出的问题。您可以在帮助中心找到有关编写良好答案的更多信息。 - Community

-4

15
也许这是一个愚蠢的问题,但是在使用这个软件包时,我仍然需要将密钥放在我的源代码中,因为它们最终必须添加到安全存储中,以便稍后读取。或者我对此有什么误解吗? - Thomas
12
我认为你的理解是正确的。那个软件包的作用是,如果你从用户处获取了一些机密信息并希望将它们安全地存储起来,那么这个软件包就会有所帮助。 - harm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接