GitHub Actions 作業是竊取機密的好地方。它持有部署金鑰、registry 權杖與雲端憑證,而且會執行別人一分鐘前才寫好的程式碼:第三方 action、套件及其安裝腳本。GitHub 的機密處理機制能防範意外,但它的設計並不是為了阻止已經在作業內部執行的程式碼。
本文說明 GitHub Actions 中機密的運作方式、實務上機密外洩的五種途徑,以及阻斷每一種途徑的控管措施。
GitHub Actions 機密的運作方式
機密存在於三個層級:組織、儲存庫與環境。當同一個名稱存在於多個層級時,範圍最具體的優先:環境優先於儲存庫,儲存庫優先於組織。
幾項值得了解的限制與規則:
- 大小與數量。 最多 1,000 個組織機密、100 個儲存庫機密與 100 個環境機密,每個最大 48 KB。工作流程可以看到所有儲存庫與環境機密,但組織機密只能看到依字母順序排列的前 100 個。
- **Fork 什麼都拿不到。**除了
GITHUB_TOKEN之外,機密不會傳遞給由 fork 的 pull request 觸發的工作流程。 - **Dependabot 被視同 fork。**由 Dependabot 觸發的工作流程只會取得唯讀權杖與 Dependabot 機密,而不會取得 Actions 機密。
- 可重複使用的工作流程需要傳入機密。
secrets: inherit會在同一個組織或企業內隱含地傳入所有機密。環境機密無法透過workflow_call傳遞。 - **環境可把關存取。**設定必要審查者後,作業必須在有人核准後才能讀取環境的機密。部署分支規則則限制哪些分支能使用該環境。
遮罩能做什麼,又不能做什麼
GitHub 會在日誌中遮蔽機密值,但遮蔽仰賴找到完全相符的內容。GitHub 自己的文件列出了遮蔽失效的情況:
- 位於 JSON 或 YAML 等結構化資料中的機密
- 從機密衍生的值,例如用一個權杖換來的另一個權杖,除非您以
::add-mask::登錄它 - 經過轉換的機密,例如經 base64 編碼
遮罩是防止意外印出機密的防護措施。對於先將機密編碼的程式碼,它完全無效。
機密如何從工作流程外洩
1. 遭入侵的 action 將機密印出
2025 年 3 月,攻擊者入侵了 tj-actions/changed-files(CVE-2025-30066),這是一個被超過 23,000 個儲存庫使用的 action。攻擊者將該 action 既有的版本標籤移到一個惡意 commit,因此每個引用 @v45 等標籤的工作流程都執行了攻擊者的程式碼。只有固定到完整 commit SHA 的工作流程倖免。
惡意酬載會從執行器的記憶體讀出機密,並印到作業日誌中,而且經過兩次 base64 編碼,讓遮罩無法攔截。在公開儲存庫上,任何人都能讀取這些日誌。這次入侵源自另一個 action reviewdog/action-setup(CVE-2025-30154),而更早之前,則是一個透過 pull_request_target 工作流程竊取的個人存取權杖。
2. pull_request_target 工作流程執行不受信任的程式碼
pull_request_target 會在基礎儲存庫的脈絡中執行,擁有其機密與可寫入的權杖,即使 pull request 來自 fork 也一樣。只要工作流程從不執行 pull request 中的程式碼,這就是安全的。但當工作流程 checkout pull request 的 head 並加以建置時,任何能開啟 pull request 的人都能讀取該工作流程擁有的所有機密。GitHub Security Lab 將這類情況稱為「pwn request」。
GitHub 一直在收緊這方面的限制。自 2025 年 12 月起,pull_request_target 一律使用預設分支中的工作流程檔案。自 2026 年 7 月起,actions/checkout 會拒絕在使用已知危險模式的 pull_request_target 工作流程中 checkout fork 的程式碼;而自 2026 年 9 月起正式推出的工作流程執行保護,將自 2026 年 11 月 2 日起,在沒有事件政策的公開儲存庫中預設停用 pull_request_target。
3. 不受信任的輸入被注入腳本
工作流程運算式會在 shell 執行之前展開。像這樣的步驟:
- run: echo "Title: ${{ github.event.issue.title }}"
會執行 issue 標題中的任何內容。攻擊者只要開一個以 shell 指令為標題的 issue,作業就會執行它,而作業的機密就在觸手可及之處。請改為透過環境變數傳遞不受信任的值,讓 shell 將其視為資料:
- env:
TITLE: ${{ github.event.issue.title }}
run: echo "Title: $TITLE"
4. 快取與建置產物將機密帶出
- **建置產物。**2024 年,Unit 42 的 ArtiPACKED 研究指出,
actions/checkout預設會將GITHUB_TOKEN寫入.git/config,因此將工作區上傳為建置產物,就會讓任何能下載它的人取得該權杖。自 2024 年 9 月起,upload-artifact預設排除隱藏檔案,而actions/checkoutv6 會將憑證存放在 checkout 目錄之外,但較舊的固定版本與自訂上傳步驟仍會外洩。 - **快取。**能在儲存庫任一工作流程中執行程式碼的攻擊者,可以填滿其快取、將真正的項目擠出,並以相同的鍵植入遭汙染的項目。之後具權限的工作流程就會還原它。2026 年 5 月,這條從
pull_request_target開始、以從執行器記憶體讀出 OIDC 權杖告終的攻擊鏈,讓攻擊者發布了 42 個 TanStack 套件的 84 個惡意版本。
5. 相依套件將機密送往伺服器
安裝腳本與作業其餘部分擁有相同的存取權限。Shai-Hulud 蠕蟲於 2025 年 9 月首次出現在 npm 上,它在 npm install 期間執行,從環境中收集 npm 權杖、GitHub 權杖與雲端金鑰,並將其張貼到公開的 GitHub 儲存庫。2025 年 11 月的那一波攻擊在 preinstall 階段執行,並將受感染的機器登錄為自架執行器,讓攻擊者日後可以再回來。
這條途徑不需要您的工作流程有任何錯誤。相依關係樹中任何一處只要有一個遭入侵的版本就足夠了,而機密會經由網路外流。
如何封鎖
**以 OIDC 取代儲存的雲端金鑰。**與其將 AWS、Azure 或 Google Cloud 金鑰存為機密,不如讓作業向 GitHub 的 OIDC 簽發者請求短期權杖,再向雲端供應商兌換。信任政策決定哪個儲存庫、分支或環境可以承擔該角色,而憑證會隨作業結束而失效。作業需要 permissions: id-token: write。如 TanStack 事件所示,OIDC 權杖仍可能從執行中的作業被竊取,但遭竊的短期權杖,價值遠低於永不過期的金鑰。
**將 action 固定到完整的 commit SHA。**GitHub 的文件稱這是將 action 當作不可變版本使用的唯一方式。標籤可以被移動;SHA 不行。自 2025 年 8 月起,允許的 action 政策可以在整個組織中強制固定 SHA,並封鎖特定 action。Dependabot 與 Renovate 可以讓固定的 SHA 保持最新。
**只給 GITHUB_TOKEN 必要的權限。**將預設值設為唯讀,並依作業授予寫入範圍。自 2023 年 2 月起,新的組織與儲存庫預設為唯讀,但較舊的組織與儲存庫除非有人變更,否則仍維持寬鬆的預設值。請參閱 GITHUB_TOKEN 權限:為每個工作流程落實最小權限。
**將機密的範圍限定在需要它的步驟。**放在步驟 env: 中的機密只存在於該步驟的環境中。在作業層級設定的機密,則對每個步驟(包括每個第三方 action)都可見。
**將正式環境機密放在環境之後。**必要審查者與部署分支規則,讓功能分支上的工作流程或由 pull request 觸發的工作流程無法讀取這些機密。
**將 pull_request_target 與不受信任的輸入視為程式碼執行。**除非必要,否則避免使用 pull_request_target,絕不在其中建置 pull request 的程式碼,並透過環境變數傳遞事件資料。CodeQL 可以檢查工作流程中的注入與 pwn request 模式;自 2025 年 4 月起已正式支援 GitHub Actions。
**啟用機密掃描與推送保護。**它們能攔截被提交到儲存庫的機密,這和上述的外洩途徑不同,但同樣常見。
**限制作業可以連線的對象。**大多數作業只需要連線到少數目的地:套件 registry、容器 registry 與幾個 API。若作業只能連到這些地方,被惡意程式碼讀取的機密就無處可去。請參閱對外連線政策如何阻止從 GitHub Actions 竊取資料。
**任何外洩後都要輪替。**若工作流程執行過遭入侵的 action 或套件,請假設它能讀取的每個機密都已被取走。全部撤銷並重新核發,而不只是您認為被使用過的那些。
CRACI 的定位
CRACI 在自己的執行器上執行您的 GitHub Actions 作業:您將 runs-on 改為 craci,工作流程、機密與執行歷程都仍保留在 GitHub 中。它有兩項功能正是針對上述外洩途徑。
- **失效即封鎖(fail closed)的對外連線(egress)政策。**您可以設定作業可連線的套件來源、registry 與主機。政策會在作業開始前驗證,並在執行器上強制執行,因此連往其他任何地方的連線都會遭到封鎖,而在違反政策時您也會收到警示。
- **作業行為的紀錄。**CRACI 會記錄每個作業實際擷取的每個套件(包括從快取還原的套件),以及作業網路連線的追蹤紀錄。當下一個遭入侵的套件公布時,您可以看出哪些建置拉取了它、連線到了哪裡,而不必從 lockfile 猜測。
每個作業都在自己的虛擬機器中執行,因此前一個作業留下的任何東西,都不會出現在下一個作業中。CRACI 不會掃描您程式碼中的機密;這部分請繼續使用 GitHub 的機密掃描。
預約示範,查看您自己其中一次建置的紀錄。
