API 密钥管理和安全性

1. 简介

过去,在其他方法不可用或被认为不方便时,Google API 密钥用于访问 Google API。热门用例包括访问 Google 地图 API 和 Firebase 公开的 Google API。随着 AI 模型、Gemini 等 AI 智能体、AI Studio 和智能体开发套件等代理开发框架的推出,API 密钥已成为访问 Google 大语言模型的主要方法。

API 密钥提供的保护级别较低。虽然 Google Cloud 提供了多种方法来防止密钥被滥用,但拥有有效的 API 密钥即可访问 Google API,而无需进行任何额外的身份验证或授权验证。有关限制 API 密钥使用的方法,请参阅文档。Cloud 博客中有关保护 Gemini 和 Google API 密钥的帖子提供了有关 API 密钥维护的其他建议。在此 Codelab 中,您将实践这些建议。

您将执行的操作

  • 查看在 Google Cloud 上创建新 API 密钥时强制执行的限制
  • 编目您的所有 API 密钥,并找出未受安全保护的密钥
  • 根据现有 API 密钥的使用情况强制执行限制
  • 定义在出现异常使用情况时删除密钥的自动化流程

所需条件

  • 新版网络浏览器(例如 Chrome)。
  • Google 账号

2. 设置

本 Codelab 中的说明假定您在 Google Cloud 控制台的 Cloud Shell 中运行命令。如果您在本地环境中安装了 gcloud CLI,则可以在本地环境中运行这些命令。

虽然可以使用 Cloud 控制台界面完成这些步骤中的操作,但方法有所不同。此 Codelab 使用命令行界面来简化互动,并更轻松地与现代 AI 智能体(例如 Antigravity CLI)集成。

启动 Cloud Shell 终端

  1. 在新浏览器窗口中使用 https://console.cloud.google.com/ 打开 Google Cloud 控制台。建议使用 Chrome 以获得最佳用户体验。
  2. 在 Google Cloud 中登录您的 Google 账号。
  3. 点击 Google Cloud 控制台顶部的激活 Cloud Shell “激活 Cloud Shell”图标
    如果显示,请在弹出的窗口中执行以下操作:
    • 继续完成 Cloud Shell 信息窗口中的设置。
    • 授权 Cloud Shell 使用您的凭据进行 Google Cloud API 调用。

选择 Google Cloud 项目

打开 Cloud 控制台后,系统会验证您的身份,并且通常会显示一个项目选择器供您选择项目。项目 ID 是一个包含 6 到 30 个字符的序列,由小写字母、数字和短划线符号组成,例如 qwiklabs-gcp-04-3075fc9fd77f。Cloud Shell 终端会使用所选项目配置 gcloud CLI。您将看到类似于以下内容的输出:

Your Cloud Platform project in this session is set to qwiklabs-gcp-04-3075fc9fd77f

这意味着,您后续向 gcloud 发出的命令将使用项目 ID qwiklabs-gcp-04-3075fc9fd77f

将项目 ID 设置为环境变量 PROJECT_ID。您可以使用以下命令查看所有项目的列表:

gcloud projects list
  • 如果您想使用与 gcloud 中配置的项目 ID 不同的项目 ID,请替换 your-project-id 并运行该命令。
    export PROJECT_ID="your-project-id"
    
    例如:
    export PROJECT_ID="qwiklabs-gcp-04-3075fc9fd77f"
    
  • 如果您想使用所选项目 ID,请运行以下命令:
    export PROJECT_ID=$(gcloud config get project)
    

3. 限制新的 API 密钥

过去,用户可以创建完全不受限制的 API 密钥。不受限制的密钥可用于调用创建该密钥的项目中启用的任何 Google API。虽然 Google Cloud 控制台会阻止用户创建不受限的密钥,但用户仍可以使用 gcloud CLI 或直接 API 调用来创建不受限的密钥。

以下步骤展示了如何创建受限 API 密钥,以限制其只能用于特定 API 和指定网站。

  1. 如需创建仅限用于 Google Map 地理定位 API 的新 API 密钥,请在 shell 终端中运行以下命令:
    gcloud services api-keys create --key-id=restricted-api-key \
      --display-name="restricted api key" \
      --api-target=service=geolocation.googleapis.com \
      --project=${PROJECT_ID}
    
    此命令会创建一个新的 API 密钥,该密钥只能用于调用 Google 地理定位服务。
  2. 添加应用限制可提高密钥安全性。将密钥的使用范围限制为仅限网站 example.com 中的所有路径。运行以下命令,将应用限制添加到密钥:
    gcloud services api-keys update restricted-api-key \
      --location=global \
      --allowed-referrers="example.com/*" \
      --project=${PROJECT_ID}
    
    您可以不将密钥的使用范围限制为特定网站,而是使用 --allowed-application 定义允许的 Android 应用allowed-ips 定义允许的 IP 地址。如需查看所有选项,请参阅完整的文档

清理

除非您打算使用自己创建的 API 密钥,否则请将其删除:

gcloud services api-keys delete --key-id=restricted-api-key \
  --project=${PROJECT_ID}

4. 编目 API 密钥

在此步骤中,您将使用 gcloud CLI 获取 API 密钥清单。生成的列表会显示您有权访问的所有有效(未删除)API 密钥。

  1. 运行以下命令可查看所有密钥名称、ID 和创建日期:
    gcloud services api-keys list --project=${PROJECT_ID} \
      --format='value(displayName,name.basename(),createTime.date())'
    
    输出将显示密钥的人类可读名称、密钥 ID 和密钥创建日期。它类似于以下内容:
    api key 1	api-key-1	2024-05-10T07:53:24
    api key 2	api-key-2	2025-06-12T14:47:57
    
  2. 选择一个密钥 ID,然后粘贴以下命令以检查该密钥是否受到任何限制。将 your-key-id 替换为所选密钥 ID 的值:
    gcloud services api-keys describe "your-key-id" --project=${PROJECT_ID}
    

输出(采用 YAML 格式)将在 restrictions 下包含一个限制列表。

createTime: '2024-05-10T07:53:24.986528Z'
displayName: api key 1
etag: W/"u1WuY41K2tPKUZd7cfLoKg=="
name: projects/123456789012/locations/global/keys/api-key-1
restrictions:
  apiTargets:
  - service: geolocation.googleapis.com
  browserKeyRestrictions:
    allowedReferrers:
    - https://example.com/*
uid: 1a2b3c4d-1234-abcd-1234-a1b2c3d4e5f6
updateTime: '2024-05-10T07:53:24.071228Z'

请注意,如果密钥从未更新,则 createTimeupdateTime 字段将具有相同的时间戳。

  1. 下载并运行该脚本,该脚本会遍历您的所有项目,并输出未设置限制的所有 API 密钥:
    curl -fsSL -o unrestricted_api_keys.sh \
      "https://github.com/GoogleCloudPlatform/devrel-demos/blob/main/security/api-key-audit/unrestricted_api_keys.sh"
    chmod +x unrestricted_api_keys.sh
    ./unrestricted_api_keys.sh
    
    运行脚本后,您将看到如下格式的输出:
    DISPLAY NAME    KEY ID    PROJECT ID    CREATION DATE
    Key 1    1a2b3c4d-1234-abcd-1234-a1b2c3d4e5f6    my-project-1    2024-05-10T07:53:24.071228Z
    
    您可以在 GitHub 上 devrel-demos 代码库的 Security 文件夹中找到此 Codelab 中使用的所有脚本。

5. 了解 API 密钥的使用情况

在此步骤中,您将查询 Google Cloud 指标,以帮助您了解哪些 API 是使用您的 API 密钥调用的。借助此信息,您可以查看密钥的当前使用情况,并根据实际信息为密钥应用 API 限制,而不是猜测。

  1. 使用您在上一步中使用的相同密钥 ID,或选择其他密钥 ID。在以下命令中,将 your-key-id 替换为所选的密钥 ID:
    export KEY_UID=$(
       gcloud services api-keys describe "your-key-id" \
       --format='value(uid)' \
       --project=${PROJECT_ID})
    
  2. 将搜索范围设置为回溯一年的使用情况历史记录。如果您想查找更长或更短时间段内的信息,请将 365(天数)替换为其他正数。
    export DAYS=365
    
  3. 刷新应用默认凭据 (ADC),以直接调用 Cloud Monitoring API。运行以下命令,并按照终端中的说明操作:
    gcloud auth application-default login
    
  4. 运行以下命令,向 Cloud Monitoring API 发送服务使用情况指标数据请求:
curl -s -G -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  --data-urlencode "filter=metric.type=\"serviceruntime.googleapis.com/api/request_count\" AND resource.labels.credential_id=\"apikey:${KEY_UID}\"" \
  --data-urlencode "interval.startTime=$(date -u -d "${DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)" \
  --data-urlencode "interval.endTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  "https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries" \
  | jq -r '.timeSeries[]?.resource.labels.service' | sort -u

该命令会查询 serviceruntime/api/request_count 内置指标,以获取标签 credential_id 与所选 API 密钥唯一 ID 匹配的数据点。然后,它会检索 service 标签的值,并在消除重复项的同时打印这些值。

API 密钥安全加固

在此步骤中,您将使用之前步骤中收集的信息,根据使用情况信息更新 API 密钥的限制配置。

您将使用上一步中使用的同一 API 密钥。如有必要,请重新运行上一步中的说明,以确保已设置环境变量 PROJECT_IDKEY_UIDDAYS

  1. 运行以下命令,以检索使用 API 密钥调用的 Google API 的列表:

SERVICES=$(curl -s -G -H "Authorization: Bearer $(gcloud auth application-default print-access-token)"
–data-urlencode "filter=metric.type="serviceruntime.googleapis.com/api/request_count" AND resource.labels.credential_id="apikey:${KEY_UID}""
–data-urlencode "interval.startTime=$(date -u -d "${DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)"
–data-urlencode "interval.endTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
"https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries"
| jq -r ‘.timeSeries[]?.resource.labels.service' | sort -u)

1. Build the list of arguments to restrict the API usage for the API key based
on the retrieved list.

```shell
API_TARGET_ARGS=()
for SERVICE in $SERVICES; do
  API_TARGET_ARGS+=("--api-target=service=${SERVICE}")
done
  1. 替换非空列表的受限 API 列表:
    if [ ${#API_TARGET_ARGS[@]} -gt 0 ]; then
        gcloud services api-keys update "projects/${PROJECT_ID}/locations/global/keys/${KEY_UID}" \
        ${API_TARGET_ARGS}
    fi
    

6. 定义异常使用情况检测

之前的步骤介绍了如何探索和强化 API 密钥。此步骤介绍了如何借助监控提醒自动响应密钥使用量的意外高峰。

以下说明将创建一个提醒,当使用 API 密钥的 API 调用速率比过去 5 分钟增加 10% 以上时,系统会触发该提醒。系统已配置提醒,以触发 Cloud Build 脚本来删除 API 密钥,防止进一步使用。您可以在接下来的 30 天内恢复密钥。如需了解如何恢复密钥,请参阅文档

这些说明会重复使用您在前面步骤中使用的变量 PROJECT_IDKEY_UID。如果您想选择其他密钥和/或项目,请按照“设置和发现 API 密钥的使用情况”步骤中的说明,为这些变量设置新值。

  1. 运行以下脚本以创建提醒政策文件:
    cat <<EOF > alert_policy.json
    {
      "displayName": "Credential API Request Count Increase Alert (Project: ${PROJECT_ID})",
      "combiner": "OR",
      "conditions": [
        {
          "displayName": "API Request Count Increase > 10% in 5m with Min Volume",
          "conditionPrometheusQueryLanguage": {
            "query": "(sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m])) / (sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m] offset 5m)) or on() vector(1)) > 1.10) and (sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m])) > 50)",
            "duration": "0s",
            "evaluationInterval": "60s"
          }
        }
      ],
      "enabled": true
    }
    EOF
    
    提醒政策使用以下 PromQL 过滤条件来触发提醒:
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id="API_KEY_UID"}[5m])
     ) /
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id="API_KEY_UID"}[5m] offset 5m)
     ) or on() vector(1)) > 1.10)
    and
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\"YOUR_CREDENTIAL_ID_HERE\"}[5m])) > 50)
    
    它会计算增长率,并将其与之前的窗口进行比较。只有在实际支出比目标支出高出 10% 以上时,才会触发提醒。为避免在调用总数可忽略不计时触发提醒,该提醒设置了触发条件,即窗口中的 API 调用次数超过 50 次。为了避免在 5 分钟的速率为 0 时出现 NaN(除以零)计算,如果前一个时间段的速率为零,则用 1 替换分母。您可以更改提醒参数,例如时间段长度 (5m)、最小阈值 (50) 或 10% 增幅阈值 (1.10)。其他政策参数定义了达到条件后应触发提醒 (duration),以及应每 60 秒探测一次条件 (evaluationInterval)。
  2. 运行以下命令,创建一个将用于发布提醒通知的 PubSub 主题:
    gcloud pubsub topics create api-key-alert-notifications --project=$PROJECT_ID
    
  3. 运行以下命令,为使用 PubSub 的提醒创建通知渠道。
    CHANNEL_NAME=$(gcloud beta monitoring channels create \
      --display-name="Pub/Sub Alert Channel" \
      --type="pubsub" \
      --channel-labels="topic=projects/$PROJECT_ID/topics/api-key-alert-notifications" \
      --format='value(name)' \
      --project=$PROJECT_ID)
    
    您将在“清理”步骤中使用 CHANNEL_NAME 环境变量。
  4. 运行以下命令以创建新的监控提醒:
    gcloud monitoring policies create --policy-from-file=alert_policy.json \
      --project=$PROJECT_ID
    
  5. 运行以下命令,以授予 Cloud Build 服务删除项目中的 API 密钥的权限。
    PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
      --role="roles/apikeys.admin"
    
    可以限制 apikeys.admin 角色仅能操作 API 密钥的特定实例。如需了解详情,请参阅 IAM 条件
  6. 运行以下脚本以创建用于删除 API 密钥的 Cloud Build 构建触发器。
    cat <<EOF > trigger_config.yaml
    name: "delete-compromised-api-key"
    description: "Triggered by Pub/Sub alert to automatically delete the leaking API Key"
    pubsubConfig:
      topic: "projects/${PROJECT_ID}/topics/api-key-alert-notifications"
    build:
      steps:
      - name: "gcr.io/google.com/cloudsdktool/cloud-sdk:slim"
        args:
        - "gcloud"
        - "services"
        - "api-keys"
        - "delete"
        - "${KEY_UID}"
        - "--quiet"
    EOF
    
  7. 运行以下命令以创建新的监控提醒触发器:
    gcloud builds triggers create pubsub \
      --trigger-config=trigger_config.yaml \
      --project=$PROJECT_ID
    

您现在可以删除提醒政策和 Cloud Build 构建触发器配置文件:

rm alert_policy.json trigger_config.yaml

或者,您也可以使用 Terraform 方案设置此自动化。从 Google Cloud DevRel 代码库中的 abnormal-usage-detection 文件夹下载 Terraform 文件。该方案接受项目 ID 和 API 密钥 UID 作为输入参数,并设置您在此步骤中看到的资源和配置。

7. 清理

为避免您的 Google Cloud 账号产生意外费用,请务必删除本练习期间创建的 Pub/Sub 主题、Cloud Build 构建触发器和提醒政策。

运行以下命令以删除您创建的所有资源:

gcloud builds triggers delete delete-compromised-api-key \
  --project=$PROJECT_ID
gcloud beta monitoring channels delete $CHANNEL_NAME \
  --project=$PROJECT_ID \
  --quiet
gcloud pubsub topics delete api-key-alert-notifications \
  --project=$PROJECT_ID
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
  --role="roles/apikeys.admin"

8. 总结

在此 Codelab 中,您为 Google Cloud API 密钥实现了一个强大的端到端安全和自动化框架:

  1. 强化默认配置:您创建并限制了 API 密钥,以将访问权限仅限于必要的 API 和受信任的平台(例如特定的 HTTP 引荐来源网址)。
  2. 审核了密钥清单:您扫描了项目环境,以检测并隔离存在直接安全风险的不受限密钥。
  3. 分析后的使用情况数据:您以编程方式查询 Cloud Monitoring 指标数据,以分析历史密钥使用情况,从而能够根据经过验证的使用情况足迹来限制密钥。
  4. 自动威胁缓解:您通过将 Cloud Monitoring 提醒政策与 Pub/Sub 主题和 Cloud Build 触发器相关联,建立了一个被动“断路器”,从而可以在异常流量高峰期间自动删除遭入侵的密钥。

后续步骤

  • 对所有 API 密钥施加限制:使用您在本实验中学到的知识来检测所有部分受限或不受限的 API 密钥,并应用 API 和客户端限制,
  • 为 API 密钥设置“断路器”:通过设置在用量突然增加时自动删除密钥,进一步保护您的 API 密钥免遭意外使用。使用实验中所示的 gcloud 命令或 Terraform。考虑利用 IAM 条件来收紧权限
  • 探索监控提醒:详细了解如何使用 Google Cloud Monitoring 服务设置提醒。
  • 详细了解 Google Cloud 提供的访问权限控制功能:查看访问权限边界政策访问权限变更传播