Google花了十多年时间告诉开发者:Google API密钥(比如用在Maps、Firebase等服务里的那些)不是秘密。但现在情况变了。

最近Truffle Security的一篇分析文章揭示了一个深刻的安全问题:Google API密钥的设计初衷和后续功能扩展之间出现了根本性的冲突。

设计初衷 vs. 现实演变

Google Cloud使用统一的API密钥格式(AIza...)服务于两种本质上不同的目的:

  1. 公开标识符 - 用于项目识别和计费
  2. 敏感认证凭证 - 用于访问私有数据

多年来,Google明确告诉开发者API密钥可以安全地嵌入客户端代码。Firebase的安全检查清单明确写着"API密钥不是秘密"。

Google Maps的JavaScript文档指导开发者直接把密钥粘贴到HTML里:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>

这在当时是合理的。这些密钥被设计为项目标识符,可以通过HTTP referer白名单等(可绕过的)控制进一步限制。它们不是作为认证凭证设计的。

然后Gemini来了。

权限的悄然升级

当你在Google Cloud项目中启用Gemini API(生成语言API)时,该项目中现有的所有API密钥(包括那些已经公开嵌入在网站JavaScript里的)会静默地获得访问敏感Gemini端点的权限。

没有警告。没有确认对话框。没有邮件通知。

这造成了两个问题:

1. 追溯性权限扩展

你三年前创建了一个Maps密钥,按照Google的指导嵌入到了网站源代码里。上个月,团队里的开发者为内部原型启用了Gemini API。你那个公开的Maps密钥现在变成了Gemini凭证。任何能获取它的人都可以访问你上传的文件、缓存内容,并增加你的AI账单。没有人告诉你。

2. 不安全的默认设置

在Google Cloud中创建新API密钥时,默认是"无限制"的,意味着它立即对项目中启用的所有API有效,包括Gemini。UI上会显示"未经授权使用"的警告,但架构上的默认设置是完全开放的。

结果就是:数千个原本作为无害计费令牌部署的API密钥,现在变成了活跃的Gemini凭证,暴露在公共互联网上。

攻击者的视角

攻击很简单。攻击者访问你的网站,查看页面源代码,从Maps嵌入代码中复制你的AIza...密钥。然后运行:

curl "https://generativelanguage.googleapis.com/v1beta/files?key=$API_KEY"

得到的不是403 Forbidden,而是200 OK。从这时起,攻击者可以:

  • 访问私有数据/files//cachedContents/端点可能包含上传的数据集、文档和缓存上下文。项目所有者通过Gemini API存储的任何内容都可访问。
  • 增加你的账单:Gemini API使用不是免费的。根据模型和上下文窗口,威胁行为者最大化API调用可能每天在单个受害者账户上产生数千美元的费用。
  • 耗尽你的配额:这可能完全关闭你合法的Gemini服务。

攻击者从不接触你的基础设施。他们只是从公共网页上抓取一个密钥。

规模:2,863个活跃密钥

为了理解这个问题的规模,Truffle Security扫描了2025年11月的Common Crawl数据集——一个包含来自整个互联网的HTML、JavaScript和CSS的约700TiB大规模存档。他们发现了2,863个活跃的Google API密钥易受此权限升级向量的攻击。

这些不仅仅是业余爱好者的副业项目。受害者包括主要金融机构、安全公司、全球招聘公司,以及值得注意的是,Google自己。如果供应商自己的工程团队都无法避免这个陷阱,期望每个开发者都能正确应对是不现实的。

Google自己的密钥

Truffle Security向Google提供了来自其自身基础设施的具体示例来演示这个问题。他们测试的一个密钥嵌入在Google产品公共网站的页面源代码中。通过检查互联网档案馆,他们确认这个密钥自2023年2月以来就公开部署了,远在Gemini API存在之前。

页面上没有任何客户端逻辑尝试访问任何生成式AI端点。它仅用作公共项目标识符,这对Google服务来说是标准的。

他们通过访问Gemini API的/models端点(Google确认在范围内)测试了这个密钥,得到了200 OK响应,列出了可用的模型。一个多年前部署用于完全无害目的的密钥,在没有任何开发者干预的情况下,静默地获得了对敏感API的完全访问权限。

为什么这是一个权限升级问题

让这成为一个权限升级而不是配置错误的是事件序列:

  1. 开发者创建一个API密钥并嵌入到网站用于Maps(此时密钥是无害的)
  2. 同一项目上启用了Gemini API(现在同一个密钥可以访问敏感的Gemini端点)
  3. 开发者从未被警告密钥的权限在底层发生了变化(密钥从公共标识符变成了秘密凭证)

虽然用户可以限制Google API密钥(按API服务和应用程序),但漏洞在于不安全的默认姿态(CWE-1188)和不正确的权限分配(CWE-269):

  • 隐式信任升级:Google追溯性地将敏感权限应用于已经合法部署在公共环境(如JavaScript包)中的现有密钥。
  • 缺乏密钥分离:安全的API设计需要为每个环境使用不同的密钥(可发布密钥 vs. 秘密密钥)。通过依赖单一密钥格式处理两者,系统容易受到攻击和混淆。
  • 安全默认设置的失败:通过GCP API面板生成的密钥的默认状态允许访问敏感的Gemini API(假设已启用)。为用户图小部件创建密钥的用户在不知情的情况下生成了能够执行管理操作的凭证。

Google的回应和路线图

Google公开记录了其路线图:

  • 范围化默认设置:通过AI Studio创建的新密钥将默认仅限Gemini访问,防止意外的跨服务使用。
  • 泄露密钥阻止:他们默认阻止被发现泄露并与Gemini API一起使用的API密钥。
  • 主动通知:他们计划在识别泄露密钥时主动沟通,提示立即行动。

这些都是有意义的改进,有些显然已经在进行中。我们希望Google能更进一步,追溯性地审计现有的受影响密钥,并通知可能不知情的项目所有者,但老实说,这是一项艰巨的任务。

给开发者的建议

如果你使用Google Cloud(或其任何服务如Maps、Firebase、YouTube等),首先要弄清楚你是否暴露了。

步骤1:检查每个GCP项目的生成语言API 进入GCP控制台,导航到API和服务 > 已启用的API和服务,查找"生成语言API"。为你组织中的每个项目都这样做。如果未启用,你就不受此特定问题影响。

步骤2:如果启用了生成语言API,审计你的API密钥 导航到API和服务 > 凭据。检查每个API密钥的配置。你要找两种类型的密钥:

  • 带有警告图标的密钥,意味着它们设置为无限制
  • 明确在其允许服务中列出生成语言API的密钥

这两种配置都允许密钥访问Gemini。

步骤3:验证这些密钥没有公开 这是关键步骤。如果具有Gemini访问权限的密钥嵌入在客户端JavaScript中、提交到公共仓库中,或以其他方式暴露在互联网上,你就有问题了。首先从最旧的密钥开始。这些最有可能在旧指导(API密钥可以安全共享)下公开部署,然后在团队中有人启用API时追溯性地获得Gemini权限。

如果你发现暴露的密钥,轮换它。

更广泛的启示

我们在这里发现的模式(公开标识符静默获得敏感权限)并非Google独有。随着更多组织将AI功能添加到现有平台上,遗留凭证的攻击面以没有人预料到的方式扩展。

这提醒我们API设计中的几个基本原则:

  1. 职责分离:公开标识符和秘密凭证应该有明确的区分
  2. 权限最小化:默认应该是最小权限,而不是最大权限
  3. 变更通知:当现有凭证的权限发生变化时,用户应该得到明确通知
  4. 向后兼容性审查:添加新功能时,需要审查对现有部署的影响

API设计不仅仅是技术问题,更是安全哲学问题。当便利性和安全性发生冲突时,选择往往决定了系统的长期可靠性。


参考来源:Truffle Security《Google API Keys Weren't Secrets. But then Gemini Changed the Rules》
扫描工具:TruffleHog