API 金鑰管理和安全性

1. 簡介

過去,如果無法使用其他方法或覺得不方便,就會使用 Google API 金鑰存取 Google API。熱門用途包括存取 Google Maps API 和 Firebase 公開的 Google API。隨著 AI 模型、Gemini 等 AI 代理、AI Studio,以及 Agent Development Kit 等代理開發架構的推出,API 金鑰已成為存取 Google 大型語言模型的主要方法。

API 金鑰提供的保護等級較低。雖然 Google Cloud 提供多種方法來防止金鑰遭到濫用,但只要擁有有效的 API 金鑰,就能存取 Google API,不必進行任何額外的驗證或授權驗證。如要瞭解如何限制 API 金鑰的使用方式,請參閱說明文件。Cloud 網誌的「保護 Gemini 和 Google API 金鑰」一文,提供 API 金鑰維護的其他建議。在本程式碼研究室中,您將實際運用這些建議。

學習內容

  • 在 Google Cloud 中建立新的 API 金鑰時,請查看強制執行的限制
  • 列出所有 API 金鑰,找出沒有安全防護的 API 金鑰
  • 根據現有 API 金鑰的使用情形強制執行限制
  • 定義自動化動作,在異常使用時刪除金鑰

軟硬體需求

  • 新版網路瀏覽器 (例如 Chrome)。
  • Google 帳戶

2. 設定

本 Codelab 的操作說明假設您在 Google Cloud 控制台的 Cloud Shell 中執行指令。如果您在本機環境中安裝了 gcloud CLI,即可在本機執行指令。

雖然步驟中的作業可以使用 Cloud 控制台 UI 完成,但方法不同。本程式碼研究室使用指令列介面簡化互動,並讓您更輕鬆地與新式 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. 如要建立新的 API 金鑰,並限制只能搭配 Google 地圖地理位置 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 資料夾

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 次。為避免NaN (除以零) 計算,如果 5 分鐘的速率為 0,系統會將分母替換為 1。您可以變更快訊參數,例如時間範圍長度 (5m)、最小門檻 (50) 或 10% 增幅門檻 (1.10)。其他政策參數會定義達到條件時應觸發快訊 (duration),以及每 60 秒應探查條件 (evaluationInterval)。
  2. 執行下列指令,建立用於發布快訊通知的 Pub/Sub 主題:
    gcloud pubsub topics create api-key-alert-notifications --project=$PROJECT_ID
    
  3. 執行下列指令,為使用 Pub/Sub 的快訊建立通知管道。
    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. 摘要

在本程式碼研究室中,您為 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 條件來縮緊權限
  • 探索 Monitoring 快訊功能:進一步瞭解如何使用 Google Cloud Monitoring 服務設定快訊。
  • 進一步瞭解 Google Cloud 提供的存取控管機制:請參閱存取權界線政策存取權變更傳播