如何正确限制对 Android 和 iOS 应用程序(用 Flutter 编写)的 Google 地图(地点、地理编码)API 调用?
Posted
技术标签:
【中文标题】如何正确限制对 Android 和 iOS 应用程序(用 Flutter 编写)的 Google 地图(地点、地理编码)API 调用?【英文标题】:How to properly restrict Google Maps (Places, Geocoding) API calls to Android and iOS Apps (written in Flutter)? 【发布时间】:2021-01-22 14:11:04 【问题描述】:我有一个用 Flutter 编写的移动应用。我正在为 Places 和 Geocoding 调用 Google API。由于对这些服务的调用是通过在 url 中包含密钥来进行的,因此限制这些密钥非常重要,这样任何人都不能只是拦截并使用它们进行多次调用,并为我们在 Google 上建立一个大帐户。
API 调用示例如下:
https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&types=address&language=$lang&components=country:za&key=$apiKey 和 https://maps.googleapis.com/maps/api/geocode/json?latlng=$lat,$lon&key=$apiKey
(其中 $apiKey=我们的 Google Maps API 凭据密钥 $input、$lang、$lat 和 $lon 代表其他变量)
Google 目前允许对 API 凭据进行以下应用限制:
无 HTTP 引荐来源网址 IP 地址 android 应用 ios 应用程序当我对我的密钥(选项“无”)没有任何限制时,我的移动应用程序可以正常工作。但是,如果我选择将其限制为 iOS 应用程序(并为我的应用程序指定捆绑标识符),我会收到错误“此 IP、站点或移动应用程序无权使用此 API 密钥”。当我将密钥限制为 Android 应用程序并为 Android 应用程序指定我的包 ID 和 SHA1 签名时,也会发生同样的情况。
我错过了什么?根据https://developers.google.com/maps/api-key-best-practices#restrict_apikey,我应该可以将密钥限制为我的特定移动应用程序。谷歌地图似乎没有接听我确实是从正确的移动应用程序拨打电话。无论我是在模拟器中还是在实际设备上以调试或发布模式运行此错误,都会发生错误。 (我也从 Test Flight 测试了应用程序的 iOS 版本。当我删除限制时,我的 api 调用工作;当我将它限制为我的 iOS 应用程序的捆绑标识符时它停止工作。)还有什么我需要配置的?问题可能是应用程序是用 Flutter 编写的吗?
我发现了一些链接(如this),这些链接表明移动应用程序不应该直接在 url 中使用密钥,而应该使用我们自己的服务器作为 Google 的代理,然后我们应该限制对我们服务器 IP 的访问。这似乎是不必要的开销,因为将密钥限制为特定应用程序 ID 和平台的选项的存在表明这应该是可能的(就像我参考的 Google 文档一样)。
【问题讨论】:
在移动应用程序 (Android/iOS) 中,您使用的是 https 端点还是使用平台 SDK (developers.google.com/places/android-sdk/start)? 我正在使用 https 端点。而且开发是在 Flutter 中进行的,而不是原生的 Android 和 iOS。 【参考方案1】:我最终创建了一个代理服务器。 (更多内容见下文)。
仔细思考后,我意识到 API 没有任何方法可以辨别传入的请求是否来自特定的移动应用程序,因此我认为对特定 iOS 或 Android 应用程序的限制并不是真的可用于 https 请求。
我还调查了一些提供的颤振插件,但其中大多数似乎在后台使用google_maps_webservice。并且 google_maps_webservice 需要在应用程序代码中使用密钥或指定代理。大多数从 google_maps_webservice 派生的插件甚至不提供代理选项,并且需要指定密钥。因此,即使使用了这些插件,最终也会“放弃”您在应用程序代码中的密钥,这使得逆向工程和获取您的代码成为可能。 (它最终出现在 iOS 上的 AppDelegate.m 文件和 Android 上的 AndroidManifest.xml 中)。 Google 建议不要以这种方式提供您的 api 密钥。
所以我最终创建了一个代理服务器。在没有密钥的情况下向我的代理发出请求,然后代理在添加密钥后将请求传递给 Google。然后代理将响应返回给应用程序。应用程序和应用程序与服务器之间的流量从不包含密钥。
幸运的是,我的应用程序已经在使用 AWS,并且用户正在使用 Cognito 登录。所以我可以在 API Gateway 中轻松创建代理,并让应用使用 Cognito 对我的代理服务器进行身份验证。这确保只有在 Cognito 中有效登录我的应用程序的用户才能调用代理,并且密钥始终保密。
【讨论】:
【参考方案2】:如果您想为特定的 Android/iOS 应用利用 Google Map Service API 配置,则需要使用依赖于 SHA 指纹和包名称或捆绑 ID 的原生 Android 和原生 iOS SDK。
由于 Google Places API 似乎很受欢迎,因此存在许多第三方尝试:https://pub.dev/packages?q=google+places
地理编码也存在类似情况:https://pub.dev/packages?q=google+geocoding
您可以查看是否适合您的应用,或者您需要创建自己的平台渠道以获得支持:https://flutter.dev/docs/development/platform-integration/platform-channels
【讨论】:
以上是关于如何正确限制对 Android 和 iOS 应用程序(用 Flutter 编写)的 Google 地图(地点、地理编码)API 调用?的主要内容,如果未能解决你的问题,请参考以下文章
限制对 Google API 的 Android 密钥的使用
我如何测试我的 xpath 已在 android/iOS 本机应用程序上识别出正确的元素
Google App Invites:具有不同包名称/捆绑 ID 的 iOS 应用和 Android 应用 - 它们如何与 goo.gl/App Invites 关联?