【2026 最新】AI Agent 密鑰安全完整教學:別讓 API Key 進入 Context 與 Session Log

最後更新: ·
AI Agent 密鑰安全教學首圖,API Key 被隔離在模型 Context 之外

你把 .env 加進 .gitignore,GitHub 上也確實看不到 API Key,於是放心讓 Claude Code 幫你除錯。它下一步執行 printenv、讀取設定檔,或把錯誤輸出貼回對話——密鑰雖然沒有進 Git,卻可能已經進入模型 context 與本機 session log。

AI Agent 密鑰安全真正要解的不是「檔案有沒有 commit」,而是 Agent 在整條工具鏈上能不能看見、記住與送出憑證。本文會用一個明確標示為假的 token,走完 Repo、環境變數、context、session log、網路出口五個暴露面,再實作 deny rule、PreToolUse hook、Claude Code 原生 mask、自架 OneCLI、短效憑證與撤銷演練。

這篇專為剛開始把 coding agent 接上 GitHub、雲端或第三方 API 的讀者寫。你不必是資安工程師;照著 lab 做完,就能判斷自己的密鑰現在在哪裡,以及下一道最值得補的防線。

先說結論:安全密鑰不是「藏好字串」

一句話定位:安全密鑰 = Agent 只拿到「使用結果」,不拿到「密鑰本體」。

.gitignore 只處理版本控制;真正有效的架構還要限制檔案與環境變數讀取、縮小網路出口、在代理層才注入密鑰,並讓憑證可以快速到期與撤銷。

如果你只用 Claude Code,2026 年最實用的起點是官方 sandbox 的 credentials.envVars[].mode: "mask";如果有多個 Agent、服務與稽核需求,再考慮 OneCLI 或企業 credential gateway。兩者都不是「把 key 變不見」,而是把真密鑰移到更小、可控的信任邊界。

AI Agent 密鑰安全:先看五個暴露面

AI Agent 密鑰安全的五個暴露面:Repo、環境變數、模型 Context、Session Log 與網路出口
把密鑰外洩路徑拆成五層,才能知道每一道防線實際擋住哪一段。

1. Repo:沒被 Git 追蹤,不等於 Agent 讀不到

先建立一個只含假 token 的練習目錄。請不要把下列字串換成真密鑰:

mkdir agent-secret-lab
cd agent-secret-lab
git init
printf '.env\n' > .gitignore
printf 'LAB_API_KEY=sk_lab_NOT_REAL_7f3c1a90\n' > .env
git add .gitignore
git status --short

git status 不會列出 .env,但檔案仍在工作目錄。Claude Code 的 Read 工具、Bash 的 cat,甚至任意 Python/Node subprocess 都可能打開它。這就是第一個觀念:Git 邊界不是執行邊界。

2. 環境變數:每個 child process 都可能繼承

LAB_API_KEY=sk_lab_NOT_REAL_7f3c1a90 \
zsh -fc 'printenv LAB_API_KEY'

輸出就是那串假 token。把密鑰從檔案搬到環境變數,確實避免了硬編碼,卻沒有阻止 Agent 啟動的 CLI、測試、hook 或 MCP subprocess 繼承它。環境變數是傳遞管道,不是保險箱。

3. Context:工具輸出會成為模型下一輪輸入

當 Claude Code 執行 cat .envprintenv LAB_API_KEY,終端上那一行不只是「你看到的輸出」。它會成為 tool result,送進模型可用的對話 context,讓 Agent 根據結果決定下一步。此時再要求模型「請忘掉 key」只是文字指令,無法證明資料沒有被處理。

4. Session Log:離開 Context 後仍可能留在磁碟

Claude Code 官方文件明確寫道,~/.claude 的 application data 是明文;經過工具的檔案內容、命令輸出與貼上文字會落進 transcript。完整訊息、tool call 與 tool result 位於 projects/<project>/<session>.jsonl,大型工具結果還可能拆到 tool-results/

這也是本文所說的 session residue:即使 context 後來壓縮、視窗關閉,假 token 仍可能留在本機 JSONL。預設會在啟動時清理超過 30 天的部分 session 資料,但 history.jsonl 不屬於這個自動清理範圍。不要把「模型現在沒提到」誤當成「磁碟上沒有」。

5. 網路出口:看得到 key 的工具,也可能送得出去

只要 Bash 可以任意呼叫 curl、套件安裝器或自寫程式,單靠 URL 字串規則很難形成可靠的出口控制。Anthropic 也提醒,curl 的參數順序、redirect、變數與協定變化都會讓 Bash URL pattern 變得脆弱。更好的設計是限制網路工具、使用 domain allowlist,並把密鑰注入範圍綁到指定 host。

防線一:Deny rule 與 PreToolUse Hook 先擋低成本失誤

Claude Code permission 規則的順序是 deny → ask → allow。先在專案的 .claude/settings.json 加上最容易理解的阻擋:

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(secrets/**)",
      "Bash(env)",
      "Bash(printenv *)"
    ]
  }
}

這些規則能擋 Claude 內建的檔案工具,也會套用到 Claude Code 能辨識的 catheadtailsed 等 Bash 檔案命令。不過官方文件也清楚說明:任意 Python 或 Node 程式自己開檔,不受 Read rule 完整約束。Deny rule 是防呆,不是 OS 級隔離。

接著加一個 PreToolUse hook,對多種工具輸入做第二次檢查。設定可合併進同一個 .claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Read|Grep",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"${CLAUDE_PROJECT_DIR}/.claude/hooks/block-secrets.py\""
          }
        ]
      }
    ]
  }
}

建立 .claude/hooks/block-secrets.py

#!/usr/bin/env python3
import json
import re
import sys

event = json.load(sys.stdin)
blob = json.dumps(event.get("tool_input", {}), ensure_ascii=False)

patterns = [
    r'(^|[^A-Za-z0-9_])\.env(?:\.[A-Za-z0-9_.-]+)?([^A-Za-z0-9_.-]|$)',
    r'(^|[^A-Za-z0-9_])secrets?[/\\]',
    r'(^|[^A-Za-z0-9_])(env|printenv)([^A-Za-z0-9_]|$)',
    r'\$\{?[A-Z0-9_]*(KEY|TOKEN|SECRET)\}?'
]

if any(re.search(pattern, blob, re.IGNORECASE) for pattern in patterns):
    print("Blocked: tool input may expose a secret", file=sys.stderr)
    sys.exit(2)

這裡有兩個容易踩的坑。第一,阻擋型 hook 要用 exit code 2;官方定義中,exit 1 對多數事件只是 non-blocking error。第二,若目標是阻止工具執行,要用 PreToolUse。PostToolUse 雖可透過結構化回傳的 updatedToolOutput,替換送給 Claude 的 tool result,但工具早已跑完,檔案/網路副作用已發生,官方也提醒 telemetry 會先捕捉原始輸出。

這個範例仍是 heuristic:編碼、別名或自寫 subprocess 都可能繞過。因此它適合攔截意外,不應被當成最終的 AI Agent 密鑰安全邊界。

防線二:Claude Code 原生 Mask,讓 Agent 只看見 Sentinel

Claude Code 2.1.199 起,sandbox 支援環境變數 mode: "mask"。sandboxed command 看見的是每個 session 不同的 sentinel;只有請求送往你列出的 injectHosts 時,sandbox proxy 才把 sentinel 換回真值。與單純 deny 相比,它讓 gh、套件管理器等工具仍能完成認證,卻不必在 command 或 log 裡拿到真密鑰。

重要:masknetwork.tlsTerminate 與 plaintext injection 設定若放在 Repo 的 .claude/settings.json,Claude Code 會忽略。請放到你控制的 ~/.claude/settings.json、管理員設定,或用 --settings 指定可信檔案。

{
  "cleanupPeriodDays": 7,
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "network": {
      "allowedDomains": [
        "api.github.com"
      ],
      "tlsTerminate": {}
    },
    "credentials": {
      "files": [
        {
          "path": "~/.aws/credentials",
          "mode": "deny"
        },
        {
          "path": "~/.ssh",
          "mode": "deny"
        }
      ],
      "envVars": [
        {
          "name": "GH_TOKEN",
          "mode": "mask",
          "injectHosts": [
            "api.github.com"
          ]
        },
        {
          "name": "NPM_TOKEN",
          "mode": "deny"
        }
      ]
    }
  }
}

為什麼一定要 tlsTerminate: {}?因為 proxy 要在 HTTPS request 內容中替換 sentinel。沒有 TLS termination 時,mask 會 fail closed:command 仍只拿到 sentinel,但上游也收到 sentinel,認證失敗。不要為了省事打開 allowPlaintextInject;官方預設為 false,因為 HTTP 上游身分未驗證,密鑰也會以明文傳輸。

我用假 Token 實測:模型究竟看到什麼?

本文在 2026 年 7 月 27 日,以 Claude Code 2.1.217、假 token sk_lab_NOT_REAL_7f3c1a90 與一次性 --settings 測試。一般 child process 直接印出假 token;打開 sandbox mask 後,同一個 printenv LAB_API_KEY 的輸出變成:

fake_value_3953eb84-21bb-4b96-83fd-f784a94cad0c

也就是說,Bash tool result 與模型 context 拿到 sentinel,不是真值。這個實測只證明「本機 command 看不到假 token」;指定網域的代理注入行為則依照官方 sandbox 文件運作,本文沒有把它誇大成整個系統的零知識證明。

還有一個常被忽略的預設:sandbox 的讀取範圍原本仍包含大部分電腦內容,連 ~/.aws/credentials~/.ssh 都要由你自行列入 credentials.files 或 denyRead。若想再剝離 Anthropic 與常見雲端供應商憑證,可在啟動時加上:

CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 claude

它是針對 Anthropic/雲端 provider 憑證的額外 subprocess scrub,不等於自動清掉每一個自訂 API Key;自訂變數仍應明確列入 credentials.envVars

敏感的一次性任務,連 Session 都不要保留

正常互動可把 cleanupPeriodDays 調短;真正敏感的一次性非互動任務,改用:

claude -p "執行受控任務" --no-session-persistence

若要在各種模式都跳過 transcript 與 prompt history,可在啟動前設定 CLAUDE_CODE_SKIP_PROMPT_HISTORY=1。官方說明指出,這類 session 不會出現在 --resume--continue 或方向鍵的 prompt history;安全與可除錯性之間要明確取捨。

防線三:自架 OneCLI,把注入點移到 Gateway

Claude Code 原生 mask 適合單一工具與本機工作流;當你同時有 Claude Code、Codex、Cursor、CI runner 或多個 Agent,集中式 credential gateway 比在每個 process 複製真密鑰更容易治理。以下以開源 OneCLI 為例。

截至 2026 年 7 月 27 日,本文查核的 OneCLI 版本為 1.43.3。先用官方單一容器啟動;這會建立持久化 volume,Dashboard 在 10254、Gateway 在 10255:

docker run --pull always \
  -p 10254:10254 \
  -p 10255:10255 \
  -v onecli-data:/app/data \
  ghcr.io/onecli/onecli

開啟 http://localhost:10254,建立 Agent、加入一個只供 lab 使用的測試 secret,並把 host/path 收窄到測試 API。接著安裝 CLI、使用 Dashboard 產生的 oc_... agent API key 登入:

curl -fsSL https://onecli.sh/cli/install | sh
onecli auth login --api-key oc_...
onecli run --dry-run -- claude
onecli run -- claude

--dry-run 不啟動 Agent,會列出執行檔路徑、將注入的環境變數名稱與 CA 路徑。正式 onecli run 則會把 gateway CA 寫到 ~/.onecli/gateway-ca.pem,注入 proxy 與 CA trust 變數,並為支援的 coding agent 產生 gateway 使用指引 skill;真密鑰由 gateway 注入,而不是寫進該 skill。

Credential gateway 信任流:Agent 看見 sentinel,Gateway 注入真密鑰,上游 API 驗證請求
Gateway 隱藏的是上游密鑰,不是消滅信任;代理 token、CA 與 gateway 本身仍是高價值資產。

觀察三個角色各自看見什麼

  1. Agent/工具:向真實 API URL 發 request,不需要持有上游 provider key;它會拿到 proxy 設定與 OneCLI agent access token 這類受限能力。
  2. Gateway:驗證 Agent、比對 host/path 與 policy,解密並注入真密鑰。由官方揭露的 HTTPS MITM 與注入流程可推論,這一層能處理解密後 request,因此是新的高信任元件。
  3. 上游 API:收到 gateway 附上真實 Authorization header 或 query parameter 的 request,回應再經 gateway 原樣返回 Agent。

因此,「Agent 看不到上游 key」是合理的產品目標;「Agent 身上完全沒有任何 credential」則不精確。OneCLI 官方架構也說明 Agent 會用 scoped access token 透過 Proxy-Authorization 向 gateway 驗證。這個 token 洩漏時,攻擊者仍可能在其 scope 與 gateway 可達範圍內呼叫能力,所以它也要最小權限、可撤銷、可稽核。

五種 AI Agent 密鑰安全方案怎麼選?

Deny rule、PreToolUse hook、Claude Code 原生 mask、OneCLI gateway 與短效憑證比較
五種方案不是互斥選項;成熟做法是由便宜的阻擋層一路疊到短效身分。

只在個人電腦用 Claude Code:先做 Repo deny rule、PreToolUse hook、sandbox mask 與窄化 allowlist。這組合成本最低,也直接處理 context 與本機 session residue。

有多個 coding agent 或共用服務:加入 OneCLI/企業 gateway,讓 provider key 集中輪替,為每個 Agent 分配不同 proxy identity,並把 host、path、rate limit 與人工批准放到模型以外的 policy layer。

正式 production 工作負載:優先使用短效、動態憑證或 workload identity。HashiCorp Vault 這類系統可發出有 TTL/lease 的 dynamic secret,並在 lease 到期或撤銷後失效。即使 session 中留下殘值,攻擊窗口也比長年不換的 static API key 小。

如果你還在建立 Agent 的整體控制面,可以先讀 AlphaLab 的 AI Agent Harness 完整解析如何建立 AI Agent Harness;密鑰只是權限、觀測、驗證與恢復其中一層。想比較 coding agent 的操作邊界,可延伸讀 Claude Code vs Codex

Gateway 新增了哪些信任風險?

  • Gateway 成為高價值目標:它在 request time 解密真密鑰,遭入侵時影響可能比單一開發機更大。
  • CA 信任被擴張:OneCLI 會把 gateway CA trust 注入 child process;保護 CA private key 與 persistent volume,避免把 CA 安裝成不必要的全系統信任。
  • Proxy identity 仍是一種能力:scoped agent token 不是上游 key,但持有人仍可能借 gateway 呼叫已授權服務。
  • 允許登入不等於允許所有動作:若只限制 domain,Agent 仍可能對同一 API 執行刪除、付款或大量讀取。要再加 method/path、rate limit 與人工批准。
  • 回應資料仍會進 Context:gateway 處理的是 credential placement,不會自動替 API response 做資料分類或遮罩。敏感客戶資料仍要另外管控。

這不是否定 gateway,而是正確描述它:gateway 把分散、難治理的 provider key,換成集中、可稽核的信任根。若你無法妥善備份、更新、監控與限制這個信任根,先用原生 mask 可能更合適。

30 分鐘 Hardening Checklist

  1. 盤點:列出 Repo 內的 .env*、credential files、環境變數、MCP 設定與 CI secrets,但不要把值貼進聊天。
  2. 阻擋:為常見路徑加 Read deny 與 PreToolUse;測試 hook 是否真的以 exit 2 阻擋。
  3. 隔離:打開 sandbox,明列 credentials.files,自訂 key 選 deny 或 mask。
  4. 窄化出口:每個 masked key 都設定最小 injectHosts;gateway 再限 method/path/速率。
  5. 縮短留存:降低 cleanupPeriodDays;敏感 non-interactive 任務使用 --no-session-persistence
  6. 掃描:在工作目錄與 Git history 跑 secret scanner,並開啟 GitHub secret scanning/push protection(方案與 Repo 類型支援範圍依 GitHub 當前規則)。
  7. 縮短壽命:能用 OIDC、workload identity 或 Vault dynamic secret,就不要發長效 static key。
  8. 演練撤銷:用假 token 或測試帳號走完偵測、revoke、rotate、verify、purge,不要等事故發生才找按鈕。

開源掃描器 Gitleaks 的目前 CLI 可先跑:

gitleaks dir . --redact
gitleaks git . --redact

第一個掃目前檔案,第二個掃 Git history;--redact 避免掃描報告再把完整秘密印出來。scanner 可能有 false positive,也不代表掃不到就安全,仍要搭配權限與 runtime 邊界。

真的疑似外洩:Revoke 要排在清 Log 前面

AI Agent 密鑰疑似外洩後的偵測、撤銷、輪替、驗證與清理流程
真正的復原順序是先撤銷能力,再換新密鑰,最後才清理殘留紀錄。
  1. Detect:確認是哪一個 key、出現在哪個 session/log/commit,以及可能被哪些主體讀到;不要在 ticket 再貼一次完整值。
  2. Revoke:先在 provider 或 gateway 撤銷舊 key/agent token。刪 log 不能讓已複製的憑證失效。
  3. Rotate:發新憑證,縮小 scope/TTL,只更新真正需要的 runtime。
  4. Verify:確認舊 key 必須失敗、新 key 只對預定 host/path 成功,並檢查異常 audit events。
  5. Purge:最後才清 Git、CI artifact、本機 transcript 與備份中的殘留,同時記錄你失去了哪些除錯/resume 能力。

Claude Code 2.1.124 以上可先預覽某個專案的本機資料清理範圍:

claude project purge /absolute/path/to/repo --dry-run

確認計畫後,才執行不帶 --dry-run 的同一命令。這會刪除該專案的 transcripts、auto memory、部分 session 狀態、相符的 prompt history 與專案設定紀錄,無法復原,也會失去舊 session 的 resume/rewind;它不是 rotation 的替代品。

❓ AI Agent 密鑰安全常見問題 FAQ

Q1:把 .env 加進 .gitignore 就夠了嗎?

不夠。.gitignore 只讓 Git 不追蹤檔案;Read、Bash、test runner 與自寫 subprocess 仍可能讀取,輸出也可能進 context 與 session JSONL。

Q2:在 CLAUDE.md 寫「不要讀密鑰」能形成安全邊界嗎?

不能單獨形成。指令能影響模型嘗試什麼,卻不改變 Claude Code 實際允許什麼。請配合 permission、PreToolUse、sandbox 或 gateway 的程式化 enforcement。

Q3:Read deny 已經擋 .env,還要 sandbox 嗎?

要,若你允許任意程式。Read deny 會覆蓋內建讀檔工具與 Claude Code 辨識的部分 Bash 命令,但任意 Python/Node subprocess 可直接開檔;OS sandbox 才能限制整個 child process。

Q4:PostToolUse 把輸出遮罩,能阻止 Key 進 Context 嗎?

可以替換 Claude 將看見的 tool result,但不是完整防線。正確回傳 updatedToolOutput 可在結果送給 Claude 前做 redaction;然而工具已讀到原值、任何副作用已發生,原始輸出也可能先進 telemetry。更穩的做法仍是 PreToolUse exit 2,或讓 runtime 從源頭只拿到 sentinel。

Q5:原生 Mask 代表 Key 永遠不離開本機嗎?

不是。目的是讓 sandboxed command 與其 log 不持有真值;proxy 仍會在指定 host 的 outbound request 中注入真密鑰,上游 API 當然需要收到它才能認證。

Q6:OneCLI 算零信任嗎?

不要直接畫等號。它把 provider key 從 Agent 移到 gateway,並提供 identity、policy 與 audit;但 gateway、CA、secret store 與 scoped agent token 都成為新的信任面。安全性取決於部署與 policy。

Q7:刪掉 Session Log,洩漏的 Key 就安全了嗎?

不安全。刪除只移除你控制範圍內的一份副本,無法收回已被複製的值。疑似曝光先 revoke,再 rotate、verify,最後才 purge residue。

Q8:短效憑證可以取代 Domain Allowlist 嗎?

不能;兩者互補。短 TTL 縮小時間窗口,allowlist/path policy 縮小可送往哪裡與可做什麼。再搭配最小 scope,才能同時縮小時間、空間與權限。

給新手的 5 個帶走重點

  1. .gitignore 解決 commit,不解決 context、session 或 egress。
  2. PreToolUse 要 exit 2;PostToolUse 無法取消已經發生的 tool call。
  3. Claude Code 原生 mask 的核心是「command 看 sentinel、proxy 對指定 host 注入真值」。
  4. OneCLI 隱藏上游密鑰,但 gateway、CA 與 scoped token 仍要被治理。
  5. 事故處理先 revoke,再 rotate/verify,最後才 purge;刪 log 不會讓 key 失效。

📚 延伸閱讀與一手資料

想把這套觀念接回日常開發,可搭配 AlphaLab 的Claude 省 Token 方法理解 context 管理,再讀Claude、Claude Code、Cowork 差異釐清各表面的執行權限。更多入門內容整理在AI 專區AlphaLab 課程

結語:把密鑰變成「可使用、不可看見」的能力

AI Agent 密鑰安全的分水嶺,不是你有沒有一個很長的 security prompt,而是 Agent 執行命令時,真密鑰究竟在不在它的記憶體、輸出與 transcript 裡。最理想的路徑是:Repo 沒有密鑰、process 只拿 sentinel、proxy 只對准許的 host 注入、憑證權限小且很快到期、每次使用都有 audit。

回到本文的 anchor:安全密鑰 = Agent 只拿到使用結果,不拿到密鑰本體。先用 native mask 把最危險的明文路徑切掉,再依 Agent 數量與治理需求決定是否引入 gateway。這比期待模型永遠不犯錯,更接近可驗證、可復原的工程。


免責聲明:本文為資安教育與防禦性教學,lab 只使用明確標示為假的 token,不構成特定系統的安全保證。功能、CLI 與預設值查核日期為 2026 年 7 月 27 日;Claude Code、OneCLI 與第三方服務可能更新,部署前請重新查閱官方文件並在隔離環境測試。本文由 AI 協助研究與整理,已由 AlphaLab 人工審閱;無 OneCLI 或 Anthropic 贊助關係。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週一封,第一時間收到新文章與投資觀察。

我們不會 spam,隨時可退訂。