Flutter 中的安全性:google_maps_flutter 需要 API 密钥,但如何安全地提供它们?

Igo*_*nko 15 google-maps api-key dart flutter google-maps-flutter

这个问题怎么重复呢?

\n

虽然 @MrUpsidedown 在评论部分提供了一些相关问题,但他们都没有真正回答我在这里试图强调的部分:如何安全地提供 API 密钥?

\n

在阅读了链接问题中的所有答案后,我倾向于自己写一个答案。

\n

语境

\n

我是一名非常新的 Flutter 开发人员,尝试将 Google 地图集成到演示应用程序中。

\n

为了节省时间,我决定尝试我能找到的最流行的 Google 地图 Widget 库,即google_maps_flutter. 该库的简洁文档显示了 Android 和 iOS 使用示例,并且都说明了 API 密钥是内联提供的,作为代码库的一部分

\n
import UIKit\nimport Flutter\nimport GoogleMaps\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GMSServices.provideAPIKey("YOUR KEY HERE") // <------------------------------------  O--\xd0\xbf\xd0\xbf\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
<manifest ...\n  <application ...\n    <meta-data android:name="com.google.android.geo.API_KEY"\n               android:value="YOUR KEY HERE"/>  <--------------------------------------  O--\xd0\xbf\xd0\xbf\n
Run Code Online (Sandbox Code Playgroud)\n

大多数开发人员都知道API 密钥必须安全存储。库文档中的代码不适合生产应用程序。Google Maps Platform 文档明确警告不要硬编码 API 密钥:

\n
\n

不要直接在代码中嵌入 API 密钥或签名机密。

\n

...

\n

不要将 API 密钥或签名机密存储在应用程序源树内的文件中。

\n
\n

问题

\n

如何正确、安全地使用这个包?有可能吗?

\n

我经常听到一个反复出现的想法:“客户端应用程序存储 API 密钥(以及类似的秘密/凭证)是不可信的因为它可以被恶意用户控制、操纵或逆向工程。 ”如果是这种情况,这是否意味着google_maps_flutter只要它需要显式提供 Google 地图 API 密钥,我就必须考虑包本质上不安全?

\n

我还知道 API 密钥限制。我肯定会使用它,但在我看来,如果 API 密钥被泄露,它只会减少“爆炸半径”。然而,我不明白这如何防止 API 密钥泄露和第三方滥用。

\n

聚苯乙烯

\n

最初,我想更广泛地提出我的问题:是否有一种方法可以在移动平台(Android 和 iOS)上安全地传递、存储和使用秘密?

\n

Igo*_*nko 19

以下是我的研究结论。

\n

客户端设备是不受信任的环境

\n

API 密钥在客户端设备上可能不安全从安全角度来看,客户端设备应被视为受到威胁。

\n

因此,以下每种方法都是不安全的

\n
    \n
  • 将 API 密钥直接存储在应用程序代码中。用户可以反编译应用程序,在调试器等下运行它,并读取API密钥。

    \n
  • \n
  • 将 API 密钥间接存储在 App 中。通过环境变量提供 API 密钥实际上没有什么不同。密钥被印在构建的应用程序的工件中,并且像以前的方法一样可读。

    \n
  • \n
  • 从受控服务向应用程序传送 API 密钥。用户可以通过篡改导致拦截来自远程服务器的接收密钥的方式来干扰应用程序。换句话说,就是经典的中间人攻击

    \n
  • \n
\n

在理想情况下,API 密钥永远不会离开您的服务器

\n

是的,你没听错。您的 API 密钥应该存在于只有您可以访问的服务中。该服务应该充当您的应用程序正在进行的所有上游 API 调用的代理。您不会将密钥暴露给客户端设备,因此它们不会从设备的环境中被窃取。

\n

然而,一个新问题立即出现:如何限制对 API 端点的访问?嗯,这是一个悬而未决的问题,超出了本答案的范围。我想指出的是,如果您为自己的服务添加自己的API 密钥,那么在某种程度上就会导致先有鸡还是先有蛋的问题。尽管如此,自己的 API 密钥还是向前迈出了一步,因为您可以限制来自特定客户端的客户端请求。换句话说,您可以确保一切都在您的掌控之中,并且不会超出上游服务(例如 Google Maps API)的配额。

\n

然而,我不知道如何可靠地识别调用者是您的应用程序(如果您需要的话)。如果我理解正确,则应用程序 ID不可靠,因为上一节 \xe2\x80\x94 客户端设备不可信。

\n

google_maps_flutter

\n

google_maps_flutter库仅在其文档中通过在应用程序中硬编码 API 密钥的方式演示了 API 密钥的配置(请参阅附录 1)。那是没有安全感的。

\n

正如有人在 flutter 的 GitHub 问题部分注意到的那样,没有任何地方google_maps_flutter建议在生产环境的应用程序中对 API 密钥进行硬编码。但是,它未能说明或建议还可以从哪里提供 API 密钥。

\n

更重要的是,它没有显示如何将代理服务器的响应/数据提供给小部件本身,这是一个巨大的问题。

\n

建议我使用 Google Maps API 服务本身来限制 API 密钥。以下是针对 Android 和 iOS 的这些限制:

\n
\n

安卓应用程序

\n

添加您的包名称和 SHA-1 签名证书指纹以限制对 Android 应用程序的使用。

\n

iOS 应用程序

\n

使用您提供的捆绑包标识符接受来自 iOS 应用程序的请求。

\n
\n

我不明白它如何能够成为一种可靠的工具。客户端手机上的 SHA-1 密钥和捆绑包标识符不能被篡改吗?!(我相信,他们可以。)

\n

原问题的答案

\n
    \n
  1. 看起来今天的图书馆消费者必须使用不可靠的密钥泄漏缓解技术。要么大多数 Flutter 开发人员不愿意制造足够的噪音来解决问题,要么一开始就不理解问题,或者干脆放弃了让它正常工作的想法。
  2. \n
  3. 目前,似乎无法google_maps_flutter以安全的方式使用它来避免 API 密钥泄露。
  4. \n
  5. 如果该库为开发人员提供了一种将 Google 地图的响应/数据直接输入到小部件中的方法,情况将会发生变化,但我没有看到任何信息表明这是针对可观察的未来的计划更改。
  6. \n
\n

附录1

\n
\n

安卓

\n

在应用程序清单 android/app/src/main/AndroidManifest.xml 中指定您的 API 密钥:

\n
<manifest ...\n  <application ...\n    <meta-data android:name="com.google.android.geo.API_KEY"\n               android:value="YOUR KEY HERE"/>\n
Run Code Online (Sandbox Code Playgroud)\n

iOS系统

\n

在应用程序委托 ios/Runner/AppDelegate.m 中指定您的 API 密钥:

\n
#include "AppDelegate.h"\n#include "GeneratedPluginRegistrant.h"\n#import "GoogleMaps/GoogleMaps.h"\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n  [GMSServices provideAPIKey:@"YOUR KEY HERE"];\n  [GeneratedPluginRegistrant registerWithRegistry:self];\n  return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n@end\n
Run Code Online (Sandbox Code Playgroud)\n
\n

  • 这是一个很好的分析。我同意你的看法。似乎大多数 Flutter 开发人员都没有意识到他们复制/读取的示例的安全隐患。 (3认同)