在貼上之前先遮罩你的秘密

遮罩一個秘密,是指在文字被分享之前,把其中的憑證——API 金鑰、權杖、密碼或私鑰——替換成一個標記,使資訊可以傳遞而不傳遞存取權。它之所以重要,是因為秘密最常見的外洩方式不是攻擊,而是一次貼上:把記錄貼進支援工單,把設定檔貼進聊天,把除錯命令貼進論壇貼文。本文解釋為什麼那些貼上如此難以撤銷,秘密遮罩工具這樣的掃描器能捕獲什麼、不能捕獲什麼,以及那套讓熱心貼上不變成事故的流程——在本機遮罩、分享遮罩副本、輪換任何已經逃出去的東西。

為什麼秘密會出現在工單、聊天和記錄裡

沒有人故意貼上秘密。他們貼上的是秘密周圍的東西:碰巧包含請求標頭的錯誤記錄,碰巧在下面第三節藏著資料庫密碼的設定檔,從終端複製出來的、權杖正坐在 Authorization 標頭裡的 curl 命令。排障靠的是上下文,而憑證就住在上下文裡——這就是為什麼支援台、聊天頻道和問題追蹤器會悄悄累積活生生的金鑰、工作階段權杖和私鑰,寫下它們的是匆忙中只盯著錯誤訊息、沒看上面那行標頭的小心的人。

然後,接收系統以傳送者從未見過的方式讓那次貼上成為永久的。工單被搜尋索引、被轉發給供應商、被匯出成報告並保存多年;聊天訊息被推送到手機通知中、被在話題中引用、被機器人總結;記錄被運送到聚合器,那裡有自己的保留期和存取清單。這樣的系統中的一個秘密不在一個地方——它在系統產生的每一份副本、備份、預覽和通知裡,其中大多數傳送者既列不全,也刪不掉。這一秒貼上、卻無法可靠召回的不對稱,正是遮罩必須發生在貼上之前、傳送者的機器上的全部理由,而不是事後的清理。

掃描器能捕獲什麼:形狀、校驗和與結構

好消息是,真實憑證高度模式化,而模式掃描是確定性的:同樣的文字總是產生同樣的發現,中間沒有任何模型在猜測。服務商金鑰帶有公開的形狀——前綴、固定長度、受限的字元集——因此 AWS 存取金鑰、GitHub 權杖、Stripe 金鑰或 Slack 權杖可以一眼被認出,就像人能在一段話中認出一個電話號碼。私鑰用其 BEGIN 與 END 裝甲行宣告自己,JSON Web Token 有剛性的三段結構,其前兩段總是以同樣的方式開頭,因為它們總是編碼同一種 JSON。

兩項檢驗讓模式層保持誠實。銀行卡號只在通過 Luhn 校驗和時才標記——那是每張真實卡內建的算術檢驗,它把真實卡與數量多得多的、只是長得像卡的長數字——訂單、發票、時間戳——區分開來。當形狀重疊時,具體的模式勝過通用的模式,一把被賦值給變數的已知服務商金鑰只報告一次,冠以它真實的類型,並附上正確的輪換說明。下方第一個來源 GitHub 自己的秘密掃描,在平台尺度上用的正是同樣的原理:合作夥伴公佈其權杖格式,平台精確匹配它們。本機掃描器是同樣的想法,只是執行在它還能幫上忙的最後一刻——你的瀏覽器裡,在你按下傳送之前。

掃描器不能捕獲什麼:熵與上下文

在服務商形狀之外,偵測就變成機率性的了。通用規則可以尋找賦值——password 或 api_key 這樣的名字挨著一個值——並可以要求那個值看起來隨機,用夏農熵來衡量。這能捕獲被賦值給變數的產生金鑰,並忽略句子中出現的 password 這個詞,但邊界是一個穿著數字外衣的判斷:強壯的人類口令和弱小的機器金鑰活在同一把尺子上,一個拼成短語的真實秘密得分很低,一個沒有佔位符措辭、看起來隨機的建置雜湊得分很高。熵能告訴你一個字串令人意外,卻不能告訴你這個字串是憑證。

上下文是更大的盲區,因為讓一個值變得危險的因素大多在值之外。工作階段 Cookie 只是一串字母和數字,直到你知道它能解鎖哪個服務。資料庫密碼藏在被拆到三個環境變數中的連接字串裡時看起來無害,一次性驗證碼只在十分鐘內敏感。掃描器也看不穿結構:一個被折到兩行記錄上的秘密、被 base64 編碼進一團資料中的秘密、或被截圖進一張圖片中的秘密,對文字模式來說是不可見的,無論模式多好。誠實的總結是:掃描器可靠地捕獲有文件、形狀良好的大多數——而它展示給你的、命中在原地醒目標示的預覽,仍然值得你自己花十秒鐘掃一眼,因為你的眼睛知道模式所不知的上下文。

還有一類是掃描器故意看不見的,而它緊挨著陷阱:佔位符。文件值——包含 example、test、dummy 或 your- 等詞——被排除,使手冊和教學不會點亮發現表格。但每一個排除都是一扇門:一個真實金鑰碰巧包含其中一個詞,或者一個懶惰的真實口令被設定成看起來像佔位符的值,都會悄無聲息地通過。把排除清單當作雜訊控制,永遠不要當作保證,也永遠不要為了讓掃描器保持安靜而給真實憑證加上佔位符措辭。

規範的偽造範例問題

每個服務商都會公佈範例憑證,其中最著名的——圍繞 AKIAIOSFODNN7EXAMPLE 構建的 AWS 文件金鑰對——形狀與真實金鑰一模一樣,因為一個看起來不真實的範例什麼也教不會。於是掃描器面對一個沒有好預設值的岔路。標記規範範例,每一份教學、教科書和培訓記錄都會變成誤報,教讀者條件反射地無視發現——警報疲勞本身就是一種漏洞。把它們加入白名單,你就等於公佈了一份任何掃描器都永不報告的字串清單,這是在邀請任何想找藏身之處的人:真實金鑰不可能等於範例,但圍繞真實金鑰的筆記、檔名或包裝,可以借用範例的偽裝。

秘密遮罩工具採取的實用答案是:標記形狀,讓發現表格開啟對話。規範範例自己的金鑰裡就包含 example 這個詞,但它仍被標記為 AWS 金鑰,因為一個服務商形狀的字串每次都值得人看一眼——看一眼很便宜,而替代方案是對任何形狀相同的東西都悄悄放行。當你編寫文件時,也要幫你讀者的掃描器和你自己的掃描器一把:使用明顯合成的值,在周圍的賦值中用佔位符措辭標明它們,並且永遠不要把真實金鑰貼進文件再改動幾個字元——被部分編輯過的真實金鑰仍然是一次憑證外洩,只是多了幾個步驟,掃描器也無法把它與真貨區分開來。

輪換,不要只是刪除——並安全地分享

如果一個真實秘密已經被未遮罩地貼出去了,操作的順序很重要,刪除排在最後——如果它還有位置的話。假設這個值已經被複製——被索引、被通知、被截圖、被同事引用——並把它當作公開的:先去服務商處輪換該憑證,吊銷舊的,使外洩的字串無論在哪裡存活都停止工作。然後才刪除或編輯原始訊息,那是衛生習慣,而不是修復本身。下方的 OWASP 秘密管理指南和 gitleaks 專案對應著這一課的兩半:管理秘密使它們短命且限定範圍,以及掃描儲存庫和歷史記錄時假設貼上已經發生。瀏覽器擷取的配套問題在在傳送那份 HAR 檔案之前中完整討論。

安全的分享流程短到可以成為習慣。第一,在本機遮罩,使用一個從不上傳原文的工具——掃描屬於你的裝置,因為把原始文字傳送給遠端遮罩服務,會在防止外洩的行為中重演外洩。第二,在工單或聊天中分享遮罩副本,它帶著排障者需要的一切——端點、用時、錯誤形狀——唯獨沒有存取權。第三,當對方確實需要憑證本身時,透過秘密管理器或一次性的、單次查看的分享連結傳送它,走與討論分開的渠道,並在工單裡說明這一點,以免有人把它貼回來。採用這三步的團隊不再依賴警惕性——那是唯一一種終將失敗的控制——而是開始依賴一種例行程序;而例行程序是讓秘密在疲憊的週五貼上中和在小心的週一貼上中一樣可靠地遠離工單的原因。

來源與延伸閱讀

常見問題

我已經把真實秘密貼進工單了。先做什麼?

先輪換,後刪除。去服務商處簽發一個替代憑證並吊銷外洩的那個,因為工單已經被索引、通知,並且可能已經被以你無法列舉的方式轉發。事後刪除或編輯訊息是良好的衛生習慣,但那不是修復——修復是讓外洩的值停止工作。

遮罩副本能讓文字完全安全地分享嗎?

更安全了,但不是安全了。遮罩移除了充當憑證的值,但文字仍然顯示主機名、端點、內部名稱、你選擇不遮罩的客戶電子郵件地址,以及你的系統形狀。請透過工作本身使用的渠道分享遮罩副本,不要發到公開貼文裡,並自己快速瀏覽醒目預覽——掃描器知道模式,但你知道上下文。

AWS 範例金鑰明明是假的,掃描器為什麼還標記它?

因為它的形狀與真實金鑰一模一樣——這正是它成為好範例的原因。一個悄悄放行每個文件形狀字串的掃描器,會把一份公佈的藏身清單交到真實憑證手裡。標記它只花在發現表格上看一眼;佔位符排除已經讓在普通賦值中被標明為樣本的值安靜下來,在那裡是措辭而不是形狀出賣了它們。