5 月 12 日 22:00(UTC),GitHub 悄悄推出了一個新的 `GITHUB_TOKEN` 結構化格式,token 字串中加入了連字號 `-` 作為分隔符。對 GitHub 來說這是個常規改版。對 PHP 生態圈來說,這個改動觸發了一顆埋了五年、所有人都不知道的地雷——Composer 在 2021 年寫下的 token 驗證 regex 不接受 `-`。當驗證失敗時,Composer 會把 token 完整內容 print 到 stderr。在 GitHub Actions 環境裡,stderr 預設會被寫進公開的 build log。
接下來 14 個小時內,任何 PHP 專案,只要 CI 用 `composer install`、`composer update`、或任何會打 GitHub API 的 Composer 子命令,build log 裡就會出現一段明文的 `GITHUB_TOKEN`。如果你的 workflow 被觸發於 `push`、`pull_request_target`、或 `schedule`,這段 log 在公開 repo 是任何人都能讀的。
這個漏洞被指派為 CVE-2026-45793,CVSS 7.5。
一、為什麼這 14 小時差點變成 PHP 史上最大供應鏈事故
`GITHUB_TOKEN` 是 GitHub Actions 預設注入到 workflow 的權杖,預設權限通常包含 `contents: write`、`pull-requests: write` 等。對攻擊者來說,拿到一支有 write 權限的 token 意味著:
- 可以對 repo push 新的 commit、新的 tag。
- 可以發布新的 release。
- 對於 Packagist 已連結 GitHub release 的套件,可以直接污染下一版的 release 內容。
如果攻擊者在這 14 小時內監聽熱門 PHP 專案的 build log(這在公開 repo 是合法可做的),蒐集到的 token 數量可能是萬等級。把惡意 commit 推到任何一個被廣泛依賴的套件(例如 monolog、guzzle、symfony 子套件),不需要 phishing、不需要社交工程,就能直接污染下游所有 `composer install` 的開發者與生產主機。
GitHub 在 5 月 13 日 14:30 UTC 暫時回滾了 token 格式變更。Composer 也在同一波修補了 2.9.8、2.2.28、1.10.28 三條 LTS 線。但這個 token 格式之後還會再次推出——這次只是給 PHP 生態圈幾天時間把 Composer 升上去。
二、立刻可以做的 5 件事
不要等下一次 GitHub 推格式時才忙著補。現在就:
- 升級 Composer:CI 鎖到 `composer self-update 2.9.8` 或以上。Dockerfile 裡 `composer:2` 標籤要明確改成 `composer:2.9.8`。
- 撤銷可能外洩的 token:到 GitHub Settings → Developer settings → Personal access tokens → 撤銷 5 月 12-13 號之間用於 PHP 專案 CI 的所有 PAT。
- 檢查 Actions log:對任何 5 月 12-13 號的 build,grep `gho_` 或 `ghs_` 或 `ghp_` 開頭的字串,找到的全部當作已外洩處理。
- 刪除公開 build log:對任何曾命中的 build,到 Actions UI 把整個 workflow run 刪掉。注意:log 即使刪除,攻擊者已經抓走的副本是回收不來的。
- 加上一道防線:在 GitHub Actions workflow 加上 `permissions:` 區塊,把 `GITHUB_TOKEN` 預設降到 `read-all`,需要 write 的 job 個別申請。這是即使下次再外洩也能止血的根本辦法。
三、給 Laravel / Symfony 團隊的額外檢查項
- 私有套件 repo(Satis / Private Packagist / GitHub Packages):檢查授權 token 是否有外洩;如有,整批 rotate。
- Composer scripts 中的 `post-install-cmd`:別在這裡 echo 任何 token 或環境變數做 debug,這類 print 也會進 build log。
- Laravel Envoyer / Forge 部署 hook:如果部署流程從 GitHub Actions 觸發,檢查中繼變數是否被 log。
- Symfony Flex recipes:upstream 沒被污染,但養成 `composer audit` 進 CI 的習慣。
四、為什麼這個事件值得寫進你的安全 SOP
這件事最可怕的地方是:沒人做錯事。GitHub 推一個更安全的 token 格式是好事。Composer 寫一個嚴格的 token 驗證是好事。把驗證失敗的細節 print 出來方便 debug 也是好事。三件「正確的事情」湊在一起,變成一個 14 小時的全球供應鏈窗口。
這是 supply chain attack 在 2026 年的新形態——攻擊面不在你的程式碼裡,而在你依賴的工具、依賴的平台、依賴的 default 設定相互作用的縫隙裡。
我的觀點
身為 PHP / Laravel 接案開發者,三件事必須馬上做:
第一,把「Composer 版本」加進你給每位客戶的 SRE 月度檢查表。過去這是 nice-to-have,現在這是合約等級的責任。
第二,所有客戶專案的 GitHub Actions workflow,今天就加 `permissions:` 預設 read-only。寫一份遷移 commit 模板,半小時可以處理 20 個 repo。
第三,學會跟客戶解釋這類「沒人做錯事但全世界差點爆炸」的事件。客戶往往認為「我們沒被駭就沒事」,你要讓他們明白「這次是運氣好,不是制度好」。能講清楚這條的接案夥伴,才有資格把單價往上提。
資料來源
- Composer vulnerability leaks GitHub tokens, threatens PHP supply chain - Sansec
- Composer 2.9.8 and 2.2.28 fix GitHub Actions token disclosure - Packagist Blog
- Urgent Update: Composer Vulnerability Leaks GitHub Secrets - SecurityOnline
- CVE-2026-45793: Anatomy of a 14-Hour PHP Supply-Chain Near-Miss - GitHub Discussion
- Composer Bug Silently Dumped GitHub Tokens Into CI Logs - Cyber Kendra