您的应用包含公开的 Google Cloud Platform (GCP) API 密钥。有关详细信息,请参阅此 Google 帮助中心文章

Posted

技术标签:

【中文标题】您的应用包含公开的 Google Cloud Platform (GCP) API 密钥。有关详细信息,请参阅此 Google 帮助中心文章【英文标题】:Your app contains exposed Google Cloud Platform (GCP) API keys. Please see this Google Help Center article for details 【发布时间】:2019-11-15 07:57:02 【问题描述】:

我的密钥使用包名和 SHA1 受到限制,Google Play 商店仍然显示此警告。

知道为什么会这样显示。我在 build.gradle 文件中定义了我的 API 密钥并从那里使用它。

【问题讨论】:

您不应该直接在 build.gradle 文件中定义您的 API 密钥 我收到同样的警告.. 任何解决方案? 你找到解决办法了吗? 不。我没有从任何地方找到任何令人满意的答案。 我认为正确的解决方案是从 API 获取密钥,并在运行中使用它。如果我们定期这样做,我们可以更改密钥。 【参考方案1】:

根据 google 的建议,设置限制,例如指定包名称和 SHA-1 密钥是要走的路。

这里已经解释过了:https://cloud.google.com/docs/authentication/api-keys#securing_an_api_key

现在,这里的问题是,无论您做什么,您的 API 密钥都将在代码库中结束,即,如果您在代码库之外指定它(通过一些属性文件)但在构建阶段通过 BuildConfig 字段将其传递(整个密钥对反编译您的代码的人可见,因为它现在是 BuildConfig 类文件的一部分)或者您将其拆分并在代码库中连接(拆分密钥仍然可见,任何人都可以通过查看使用来连接它们以获得最终密钥来自反编译的 apk)。

拆分密钥版本将消除 Play 管理中心的警告,但密钥仍然暴露。

因此,我建议的解决方案是对您的 API 密钥进行编码并将其传递给您的代码库。就在使用它之前,你将它解码回来。

一个非常简单的例子可以是:

请使用更好的编码算法而不是这个,这仅用于演示目的。这里我们使用 Base64 编码。

import android.util.Base64

fun main() 
   // API Key = "123456ABC"
   val myEncodedApiKey = "MTIzNDU2QUJD" // Should be passed via BuildConfig
   val decodedApiKey = Base64.decode(myEncodedApiKey, Base64.DEFAULT)

   // Now use `decodedApiKey` in your codebase.
   val decodedApiKeyString = String(decodedApiKey)

为什么这样更好?

    您的密钥与 GCP 项目中的密钥不完全相同。 播放控制台在扫描您的代码库时,无法将其匹配回您的 GCP 项目 API 密钥。因此没有警告。

更新(关于使用 google-services.json 文件获取 API 密钥的说明):

使用来自 google-services.json 的 API 密钥的解决方案不太有效。如果您连接您的 Firebase 帐户,通常会生成 google-services.json 文件。此处定义的 API 密钥具有不同的限制模型。您在 GCP 项目中定义的是不同的,它允许您传入包名称和 SHA-1 密钥,并限制为特定类型的 API 访问,例如仅 Youtube 访问。因此,如果要使用来自 google-services.json 的 API 密钥,那么您基本上没有使用您在 GCP 帐户中设置的限制。 GCP 帐户不会生成 google-services.json 文件。

这里有一个来自谷歌的官方文档,用于设置使用 GCP 项目定义的 API 密钥的 Youtube API,在文档中,它提到直接将密钥放入代码中。 (这无论如何都是错误的,因为它被暴露了,但那是你的谷歌)。

https://developers.google.com/youtube/android/player/setup

在任何文档中都没有提到使用 google-services.json 文件来检索 API 密钥。

【讨论】:

根据 google 指南,这是错误的,您的 api 密钥不应出现在源代码树的任何位置。在您的答案中,它仍然以加密格式存在于您的源代码中。任何人都可以使用 base64 转换器轻松解密该密钥。 我已经解释过了,但这比您在 2 个答案中建议的要好得多,后者将 API 密钥作为字符串资源或拆分版本的一部分完全公开,因此这意味着它已经部分源代码。如果您要使用私钥对其进行编码,这会使您的密钥方式比使用任何其他方法更安全。 是的,即我发布了另一个答案以从 google-service.json 文件中获取 Api 密钥作为谷歌指南,它不在源代码树中。 google-service.json 文件中有一个 API KEY,您将在 google 开发者控制台中获取该 API 密钥,以便您可以轻松地将其限制为应用级别或 API 级别 无论 google-service json 中有什么键,它都会在应用构建时添加到字符串文件中。所以最终关键将在代码库中。【参考方案2】:

有 Google Play 广告吗?至少从 19 年 8 月 1 日起,触发此操作的 api 密钥似乎嵌入在完整的 google play services 广告库中。那就是——

implementation 'com.google.android.gms:play-services-ads:18.1.1'

触发警报,但是

implementation 'com.google.android.gms:play-services-ads-lite:18.1.1'

removes some code that already exists in the play store 没有。错误本身引用了一个混淆的方法,我在 apk 中追踪到:

com/google/android/gms/internal/ads/[obfuscatedstring]/<clinit>()

这似乎设置了一堆常量。其中之一叫做gads:safe_browsing:api_key

以防万一我错了,这不是每个人的代码,我不会在这里重现它,但它在我看来确实像一个可能触发问题的 GCP 密钥。它有 40 个字符,这个相同的密钥可以在 Internet 的其他地方找到。

我的猜测是,如果您使用的是 play-services-ads 库(而不是 Firebase 库),它可能会看到此字符串并发出警报。

【讨论】:

我在 Google Play 控制台中看到了完全相同的问题,但我完全糊涂了。这似乎是谷歌代码中的错误。对吗? 这是我的猜测。我认为 play-services-ads 库中的关键是触发(错误)警报。如果这不是虚惊一场,而且他们也无意将该 API 密钥放入库中,那么他们可能应该将其删除 :)【参考方案3】:

您可以通过像这样将密钥拆分为 4 个部分来消除此警告

public static final String API_KEY_PART_1 = "Asdsdfdd-";

public static final String API_KEY_PART_2 = "dfsFdsdsFdd";

public static final String API_KEY_PART_3 = "Pgdhs_SfSfs";

public static final String API_KEY_PART_4 = "fdfDDSD";

并通过连接字符串来使用它

.attest(nonce.getBytes(), Constants.API_KEY_PART_1+Constants.API_KEY_PART_2+Constants.API_KEY_PART_3+Constants.API_KEY_PART_4)

注意:确保您的 API 密钥仅限于应用程序访问。否则,如果有人反编译您的 apk 并使用您的 api 密钥,则可能会增加您的账单。

使用 SHA 和包名称 click here 限制您的 API 密钥以了解详细信息

【讨论】:

这是正确的。我已经用包和 SHA1 限制了密钥。那么为什么谷歌会发出警告呢?因为一旦我限制了,有没有可能被误用? 是的,如果您限制它,没有人可以滥用此密钥。此警告仍在显示,因为当您上传 apk 和谷歌自动化测试检查此密钥是否存在于代码中时,它们会抛出一个暴露的密钥警告,所以现在您可以拆分此键以忽略来自 Play 商店的此警告 我厌倦了这种方法,到目前为止我还没有看到新版本的 Play 商店警告 我尝试了这种方法,但仍然显示警告消息。 有什么特别的原因将它分成4个吗?【参考方案4】:

抱歉,这里游戏有点晚了……

按照以下步骤操作:

    Firebase console 将帮助您下载 google-services.json。此外,大多数 API 的快速入门指南都有生成此文件的说明。下载 google-services.json 文件后,将其复制到 Android Studio 项目的 app/ 文件夹中,如果您使用多种构建类型,则复制到 app/src/build_type 文件夹中。 Google Cloud Console 将帮助您通过选择在 Firebase 控制台中创建的项目在 API 库部分启用 GCP(例如:Places API、Map SDK for android 等)。

    您可以通过以下方式获取 api 密钥:

    activity.getResources().getString(R.string.google_api_key);

字符串键名为“google_api_key”,用于获取 API 密钥。

仅供参考,google-services.json 文件被解析并将其值添加到您拥有的 xml(路径:app/build/generated/res/google-services/build_type/values/values.xml)访问:https://developers.google.com/android/guides/google-services-plugin

【讨论】:

有人知道我为什么会遇到这个漏洞问题,即使我一直使用这种存储方法? @fillobotto 我也有同样的问题,你知道为什么吗?【参考方案5】:

抱歉,我之前的回答有点晚了,因为我在一些研究后发现了针对此问题的适当修复,因此绕过了警告

您可以从 google json 文件中获取 api 密钥。 google-services.json 文件被解析,它的值被添加到一个 xml 中,您可以在 Java 代码click here 中访问 json 的所有元素作为 Android 资源以获取详细信息

以下是从 json 文件访问 google api 密钥的示例:

activity.getResources().getString(R.string.google_api_key);

【讨论】:

这如何有效? google_api_key 是与 GCP 项目中定义的密钥不同的密钥,并且还限制为特定的应用程序包和 SHA-1 密钥。 为什么不一样?您应该使用相同的密钥并从 Google 云控制台中删除不必要的密钥。 @Nishant。 因为来自 google-services.json 的 google_api_key 是由 firebase 项目提供的。 GCP 项目中定义的 api 密钥不会生成 google-services.json。因此,如果有人使用 google-services.json 文件中的 google_api_key,那么他们使用的是没有设置限制的 API 密钥。 它应该在您的开发者控制台中。请检查您的谷歌控制台。在您的控制台中,您可以使用包名称和 SHA-1 来限制该密钥。 这是你提到的两件事。首先,google play 控制台不提供 google-services.json。其次,我们在这里讨论的是 GCP 项目,所以你转到 Googe Cloud 控制台。现在关于 API 限制,只能在 GCP 控制台的 API 部分进行设置。此处已对此进行了很好的解释:cloud.google.com/docs/authentication/…【参考方案6】:

可能会晚,但会帮助一些人,

Firebase JSON 配置文件和谷歌没有链接 地图集成 API 密钥

Firebase 说明

如果您将 Firebase 集成到您的应用中,则需要将 JSON 配置放入您的代码中,这是将应用与 Firebase 连接的唯一方法。

但在 Google 地图中,情况就完全不同了。它将提供密钥 - 放置在我们的代码中,但不是硬编码或按原样放置,

解决方案:

我已经结合了两种解决方案来解决,从Nishant 和Sahil Arora,将密钥编码成其他东西并将它分成几部分,以使其难以解码,我分成四部分,您可以随心所欲:

解码您的 ApiKey

// getEncodedApiKey result encoded key,
// splitted into four the part
String thePartOne = "encodedPartOneMaybe";
String thePartTwo = "encodedPartNextMaybe";
String thePartThree = "encodedPartNextMaybe";
String thePartFour = "encodedPartLast";

public String getSplitedDecodedApiKey()

    return new String(
            Base64.decode(
                    Base64.decode(
                                thePartOne +
                                    thePartTwo +
                                    thePartThree +
                                    thePartFour ,
                            Base64.DEFAULT),
                    Base64.DEFAULT));

编码您的 ApiKey

//just to encode the string before splitting it into the pieces
//and remove this method from the source code
public void getEncodedApiKey()

    String plainApiKey = "xiosdflsghdfkj"; //
    String encodedApiKey = new String(
            Base64.encode(
                    Base64.encode(plainApiKey.getBytes(),
                            Base64.DEFAULT),
                    Base64.DEFAULT));
    Log.i(TAG, "getEncodedApiKey: " + encodedApiKey);

【讨论】:

【参考方案7】:

在我的情况下,在使用 google api 密钥初始化 Youtube Player 时,很长一段时间都遇到了同样的问题。

即使使用 SHA 和包名称限制 API 密钥,它也没有消失。

但是,在迁移到 androidX 后,Google Play 控制台中的安全警报消失了。

【讨论】:

【参考方案8】:

最好使用 firbase firestore 数据库来存储您的 api 密钥并访问它。 Firestore 作为 Google 的产品是安全且高度安全的。

并使用此代码访问它

mDatabase.collection(COLLECTION_NAME)
.document("DOCUMENT_NAME")
.get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() 
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) 
Log.d(TAG, "google_places_api_key: " + documentSnapshot.get("google_places_api_key") + "");

);

此方法安全可靠,任何中间人攻击都不会访问您的 api 密钥,当然 play store 将允许此方法。

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于您的应用包含公开的 Google Cloud Platform (GCP) API 密钥。有关详细信息,请参阅此 Google 帮助中心文章的主要内容,如果未能解决你的问题,请参考以下文章

您的应用包含公开的 Google Cloud Platform (GCP) API 密钥。有关详细信息,请参阅此 Google 帮助中心文章

Google FireBase - fcm 推送 (Cloud Messaging)

您的应用包含非公开 API 使用

Terraform Google Cloud:使 VM 实例 IP 公开

D1net阅闻 | Google Cloud发布公开测试版Cloud IoT Core

公开课预告:如何借助Google Cloud在海外部署音视频业务?