事件經過
n8n 是一套自動化工具,許多企業自行架在自己的伺服器上。它會保存所連接的其他系統的登入資料,而每一套設定內部都有一把主金鑰,能解開全部這些登入資料。專門尋找誤上傳金鑰的 GitGuardian,研究了一個人從一把外洩金鑰出發,能走到多遠。
團隊搜尋了發佈在 GitHub 上的程式碼,包含歷史紀錄中保留的舊版本,找到 4,576 把 n8n 設定的金鑰。這些金鑰指向 1,255 套獨立的設定。其中部分設定暴露了不只一把金鑰,因此團隊把屬於每一套設定的每一把金鑰都收齊後試用。在測試當時可連上的 896 套設定中,321 套接受了至少一把已公開的金鑰。這約為可連上設定的 36%,以及程式碼中被指名的所有設定的約 26%。可連上的設定中有 575 套不接受任何一把被試用的金鑰。
在另一項統計中,團隊使用一項全網搜尋服務,找出 31,793 套 n8n 設定。其中 4,398 套正在對外提供一個由主金鑰推算出來的識別值,占所觀察設定的 13.8%。這些之中有 129 套,其對外提供的值與早已出現在公開程式碼裡的弱主金鑰相符。團隊也回報了 n8n 產生用來簽署登入的密鑰時所存在的弱點,且到研究進行時,n8n 已被記錄 48 項安全缺陷。
團隊進一步重現了一種透過 n8n 的 Git 功能進行的攻擊,把一把屬於權限足夠高的帳號的金鑰,變成對主金鑰與加密後登入紀錄的存取權。研究之外是否有人把這些路徑用在運作中的設定上,未知;是否有任何人保存的登入資料被解開,未知。
研究進行期間,GitGuardian 曾多次直接向 n8n 揭露。n8n 確認收到回報,表示已知悉這些問題並計畫處理,後來將它們結案。n8n 在版本 1.123.10 與 2.5.0 修正了 Git 功能的缺陷。團隊自行的檢查發現,自版本 2.25.6 起,該識別值不再以同樣形式對外提供。GitGuardian 發佈時,尚未獨立確認涵蓋金鑰處理相關發現的所有變更都已釋出。
主要來源——S1:https://blog.gitguardian.com/n8n-security-encryption-key-compromise存檔快照
損失成因
- 寫在程式碼裡被公開的金鑰,在從現行檔案中被拿掉之後仍然有效,因為它只有在有人作廢那一把金鑰時才失效。 程式碼的舊版本會一直留在公開的程式碼託管平台上可被讀取,而這類金鑰本身不帶到期日。因此一把金鑰從公開的那一刻起就一直可用,直到有人刻意將它作廢;而當一套設定有好幾把金鑰流落在外,作廢被注意到的那一把,其餘的仍然可用。技術名稱:版本歷史中的長效密鑰(long-lived secret in version history)
- 每一把被公開的金鑰都帶著建立它的那個帳號的全部權限,因此讀到金鑰的人就繼承了全部權限。 這類金鑰通常是由負責接線的人建立的,而他們持有最高層級的帳號,所以單一把金鑰能觸及的範圍遠超過它原本被建立的那一項工作。持有它的人可以讀取每一個自動化流程、列出已保存的登入資料,並在伺服器上啟動新的自動化流程。技術名稱:權限過寬的憑證(overscoped credential)
- 由一套設定的主金鑰推算出來的值,被交給了完全不必證明身分的請求。 因為那個值不必登入就會被交出,對主金鑰的猜測可以在不再碰觸該設定的情況下拿去比對驗證。研究團隊把 129 套可連上的設定,比對到早已存在於公開程式碼中的弱主金鑰。技術名稱:未經身分驗證的存取(unauthenticated access)
判讀
不小心公開的金鑰,不會因為承載它的程式碼往前推進而失效;它只有在有人把那一把金鑰作廢時才失效。當同一套設定有好幾把金鑰能打開,作廢你注意到的那一把,其餘的仍然活著,而任何人都能查出哪些設定還連得上。
自我查核
只有你能回答的
- 你先前是否以為把金鑰從現行程式碼中移除就等於讓它失效?曾經有任何人告訴過你,它會一直有效,直到在自動化工具本身把它作廢為止嗎?
- 你是否假設你的自動化設定只有一把金鑰、且只有你持有?你今天能說出其他每一把是誰要求的、為什麼要嗎?
- 如果現在有一把能打開你自動化設定的金鑰正被陌生人讀到,它會觸及你哪些其他帳號、客戶資料與供應商?這樣的觸及範圍是你願意承擔的嗎?
你的 AI 可以幫你查的
僅回報,不要變更任何程式碼或設定。每一項回答都要為每個發現附上檔案或主控台的參照位置,若有無法判定之處,請明白說出。
- 搜尋本專案所擁有的每一個 repository 的完整歷史,包含所有 branch、tag、已刪除的檔案,以及匯出的 workflow JSON,尋找 n8n API key 與 n8n encryption key 的值。回報每一筆命中並附上檔案或主控台的參照位置,包含在目前 checkout 中已不存在的命中,並就每一筆說明同一個值是否仍出現在任何運作中的設定裡。
- 列出目前註冊在此實例上的每一把 n8n API key,包含其所屬者與建立來源,並回報哪些金鑰仍被接受。接著回報:在檔案被 commit 或分享之前,是否有自動化的密鑰掃描(secret scanning)跑過此 repository 的歷史與匯出的 workflow JSON,以及它是否對每一次變更都執行;請附上檔案或主控台的參照位置。
- 針對每一把使用中的 n8n API key,回報它所屬的帳號以及該帳號的角色與權限,並明確指出哪些金鑰能列出已保存的憑證物件、讀取完整的 workflow 定義、檢視執行資料,或建立並啟用 workflow。回報任何由 owner 或管理員帳號簽發的金鑰;請附上檔案或主控台的參照位置。
- 回報此 n8n 實例是否可從公開網際網路連上、對其 settings 路徑的未經身分驗證請求是否會回傳 public instance ID,以及它運行的版本與 2.25.6 相比為何。另請回報 public API 及其 Swagger playground 是否已停用,以及是否設定了一組獨立隨機產生的使用者管理簽署密鑰,而非讓驗證用的資料依賴 encryption key;請附上檔案或主控台的參照位置。
預防方式
要作廢的是金鑰本身,不只是它的副本
任何曾經被公開、貼出或匯出的金鑰,都當成仍然有效,直到你在 n8n 裡面把它作廢為止。從檔案、聊天訊息或匯出檔中刪掉它,完全不影響它是否還能打開你的設定。為這套設定曾發出的每一把金鑰保留一份書面清單,記下是誰要求的、用途是什麼,這樣清理才是照著清單一條條處理,而不是只處理那一把浮上來的金鑰。
每個自動化流程用自己的金鑰,絕不交出擁有者帳號
要求每個自動化流程都有各自的金鑰,由一個只能做該流程所需事項的帳號建立,並拒絕用那個擁有一切的帳號來連接工具。當有人替你設定新的連接時,把「你用的是哪個帳號,它能做什麼?」列入你以書面接受的交付內容。
假設你的設定會告訴陌生人的任何東西,都可能被用來驗證對主金鑰的猜測
請當初安裝這套設定的人以書面確認:它有哪些部分會在沒有登入的情況下回應陌生人;並把任何好記的主金鑰(例如你的公司名或專案名)換成一個長的隨機值,而且這個值不同時用來簽署登入。在你拿到書面說明之前,把這套設定移出公開網際網路,放在私有網路或一份嚴格的允許位址清單後面,並把連上它的系統數量維持在最少。
一把金鑰是在有人作廢它時才不再危險,而不是在它從畫面上滾出視線時。