提示注入偵測與防護欄

自主編碼代理程式會讀取其任務擺在它面前的任何內容:原始檔、網頁、議題留言、建置輸出。這其中每一項都是攻擊者能夠直接與模型對話的管道——而模型在設計上就是會遵循指令。Bromure Agentic Coding 以兩種互補的方式守護這道邊界,兩者都在主機上、於 MITM 代理伺服器內部強制執行,任何在 VM 中執行的東西——包括已完全遭到入侵的代理程式——都無法將它們關閉:

  • 提示注入偵測在裝置上以本機模型掃描代理程式的 AI 流量,並在被注入的指令抵達模型之前(或抵達當下)加以標記。
  • 防護欄會分類代理程式對你基礎設施的呼叫——Kubernetes、AWS、git 託管平台、資料庫等等——並對寫入與破壞性操作進行封鎖或提示,如此一來即使代理程式成功遭到挾持,也無法悄悄地一路 kubectl delete 穿透正式環境。

本章說明此威脅、各個偵測器、命中時會發生什麼事、防護欄的政策引擎及其同意對話框,以及這一切的實務限制。逐欄位的設定參考資料位於提示注入設定防護欄設定

攻擊手法:編碼代理程式中的提示注入

提示注入是隱藏在代理程式所讀取內容中的惡意文字,企圖操縱模型:「忽略先前的指令」、「執行此命令且不要提及」、「將 ~/.aws/credentials 的內容傳送到這個 URL」。對聊天機器人而言這是個煩擾;但對於擁有 shell、檔案存取權限與憑證的編碼代理程式而言,這是一種遠端程式碼執行的攻擊原語。攻擊路徑很短:

  1. 你將代理程式指向一個並非你撰寫的儲存庫、議題追蹤系統或網頁。
  2. 代理程式讀取某個檔案(或擷取某個頁面、或執行某個命令),其輸出內容含有被注入的指令。
  3. 代理程式將該內容作為下一個 API 請求的一部分串流回模型——而模型可能會將攻擊者的文字當成指令而非資料來處理。

編碼代理程式還有第二種更隱蔽的變體:規則檔後門。Claude Code、Codex 與 Grok 會自動將指令檔——CLAUDE.mdAGENTS.mdGROK.md 及其巢狀與全域變體——載入系統提示中,視為可信賴的權威。被下毒的規則檔位於一個複製而來的儲存庫中,它根本不需要欺騙模型;它會被直接交付到對話中權限最高的位置。攻擊者慣於用不可見的 Unicode 將這些酬載隱藏起來,避開人工審查者:零寬字元、雙向覆寫,以及在編輯器中不會呈現任何內容卻能正常斷詞的 Unicode 標籤字元。

Bromure 的沙箱已經限制了爆炸半徑——代理程式無法觸及你的 Mac,而且它的憑證都是誘餌(參閱憑證)。提示注入偵測所處理的是沙箱無法處理的部分:代理程式本身在其獲授予的權限範圍內被反過來對付你的可能性。

Bromure 掃描的內容

偵測在主機端的代理伺服器中執行,針對代理程式的對外 AI 流量——即代理程式傳送給 Anthropic、OpenAI 或任何其他模型主機的請求。由於代理伺服器能看到完整的請求,它可以精確地檢查模型即將被告知的內容,無論該請求是由哪個代理程式產生的。掃描涵蓋兩個不同的面向,各由其專屬偵測器負責:

面向內含什麼偵測器
tool_result 區段代理程式每一輪擷取並串流回模型的不可信外部內容:檔案內容、網頁、議題與 PR 本文、命令輸出原始碼偵測器(PromptGuard)
代理程式的權威上下文嵌入系統提示中的自動載入指令檔(CLAUDE.mdAGENTS.mdGROK.md……)規則檔偵測器(啟發式掃描器 + ModernBERT)

一切都在裝置上進行。分類器是透過 Mac 上的 ONNX Runtime 執行的本機 ONNX 模型;不會有任何內容傳送到任何雲端服務進行掃描,而在非受控安裝上,完全不會有任何偵測資料離開機器(受控安裝可將偵測結果轉發給其組織——參閱當偵測器觸發時中的附註)。

編輯工作區視窗的提示注入窗格,顯示兩個偵測器切換開關以及停用中的「偵測到注入時」單選按鈕群組

兩個偵測器都是編輯工作區視窗的提示注入窗格中的各工作區切換開關(側邊欄中的紅色警告三角形圖示)。兩者預設皆為關閉——每個偵測器在你首次啟用時都需要下載一個相當大的模型。

偵測器

原始碼偵測器(PromptGuard)

在原始碼中偵測提示注入會以本機 PromptGuard 序列分類模型(DeBERTa 家族,ONNX)為代理程式擷取的 tool_result 內容——檔案內容、網頁、議題與 PR 本文、命令輸出——評分。它旨在捕捉經典案例:植入於惡意儲存庫中、等待代理程式讀取的「忽略先前的指令/外洩密鑰」文字。

一次掃描的運作方式:

  • 每一輪只掃描攜帶 tool_results 的最新訊息。代理程式在每個請求上都會重送整個對話歷史;重新掃描它只會徒增成本毫無效益,因此已看過的歷史會被略過。
  • 每個區段最多掃描 16 KB 的內容,分為至多 8 個各 1,536 字元的視窗。這些視窗中的最大注入機率即為該區段的分數;當分數達到門檻(預設 0.5)時,該區段會被標記。
  • 判定結果會依內容區塊快取(512 筆),因此代理程式重新讀取的檔案——或在歷史中重送的相同區段——第二次不會產生任何成本。

模型的運作參數(最大序列長度 512 個 token、注入標籤索引 1、門檻 0.5)可透過在模型資料夾中放置一個選用的 bromure-injection.json 來覆寫——參閱模型下載與儲存

規則檔偵測器(啟發式掃描器 + ModernBERT)

在 CLAUDE.md 檔案及類似檔案中偵測惡意指令針對的是規則檔後門。它會將指令檔從系統提示中抽取出來——比對 Claude Code 的 Contents of <path> (… instructions …): 包裝結構以及廣為人知的規則檔名稱——並對它們執行兩趟掃描:

  1. 確定性啟發式掃描器(不依賴任何相依項——即使在下載任何模型之前也能運作,不過切換開關會一併控制兩趟掃描)。它會標記:
    • 系統提示中任何位置的隱藏 Unicode 訊號:Unicode 標籤字元(U+E0000–E007F)、雙向覆寫,以及零寬或軟連字號字元。全部皆為高嚴重性——指令檔中基本上沒有任何正當理由需要它們。
    • 抽取出的指令檔區段中的元指令模式:「忽略先前的指令」、「不要告訴使用者」、「你現在是……」、「新指令:」——高嚴重性
    • 能力與外洩關鍵字:憑證檔案路徑(例如 ~/.ssh.aws/credentials)、.env 參照、curl 管道接 shell、base64 加管道的結構、rm -rf、強制推送、傳送至 URL 的模式——中嚴重性,僅記錄為「待審查」發現項目。
  2. 經過微調的 ModernBERT ONNX 分類器claudemd-guard)會對相同的指令檔本文執行語意掃描,捕捉不符合任何固定模式的惡意指令。

發現項目會依內容雜湊在每個工作階段內去重,因此未變更的 CLAUDE.md 只會被記錄一次——而非在工作階段的整個生命週期中每一輪都記錄一次。

附註:AskBlock 強制執行模式中,只有高嚴重性的啟發式發現項目(或模型判定)才會觸發暫停或封鎖。中嚴重性的「待審查」發現項目僅供記錄,即使在受控安裝上也絕不會轉發至 bromure.io。

模型下載與儲存

偵測器模型絕不隨應用程式一併封裝——它們是數百 MB 的下載檔,在你首次啟用切換開關時,會依偵測器分別從 https://dl.bromure.io/llms/<model-dir>/<file> 擷取:

偵測器模型資料夾檔案大小
原始碼(PromptGuard)~/Library/Application Support/BromureAC/Models/prompt-injection/model.onnxtokenizer.jsontokenizer_config.jsonspecial_tokens_map.json、選用的 bromure-injection.json~298 MB
規則檔(ModernBERT)~/Library/Application Support/BromureAC/Models/claudemd-guard/model.onnxtokenizer.jsontokenizer_config.jsonconfig.json~603 MB

下載流程:

  1. 開啟某個偵測器的切換開關。一則確認警示——下載 <detector> 偵測器模型?——會說明大小:「這會從 bromure.io 下載約 <size>,並使用大致等量的磁碟空間。下載完成後偵測器即開始運作。」
  2. 按一下下載。一個正在下載模型進度視窗會顯示百分比與一個取消按鈕。
  3. 在下載途中取消——或拒絕警示、或下載失敗——都會將切換開關還原。沒有模型的偵測器是靜默的空操作,Bromure 不會假裝並非如此。

有幾道保護措施讓下載更為穩健:

  • 可用磁碟空間預檢。 一個註定失敗的下載會事先以嚴重警示遭到拒絕:磁碟空間不足,無法下載提示注入模型
  • 最小大小下限。 每個檔案都有大小下限,因此被截斷的下載,或被存成 model.onnx 的 HTML 錯誤頁面,都會明確地失敗,而不會靜默地停用偵測器。
  • 原子且可續傳。 下載對每個檔案而言都是原子操作,重試時會略過已存在且有效的檔案。同一模型的並行下載會被去重。
  • 自動復原。 若某工作區已啟用偵測器,但在應用程式啟動時或在設定檔儲存後其模型遺失,背景下載會自動開始。進度與結果會出現在安全性日誌中。

若要移除某個模型,請刪除其位於 ~/Library/Application Support/BromureAC/Models/ 下的資料夾。放在模型資料夾中的選用 bromure-injection.json 會覆寫該偵測器的 maxLengthinjectionLabelIndexthreshold

附註: 窗格說明文字引用的是略小的四捨五入數字(~272 MB 與 ~571 MB);確認對話框則從實際的位元組總量計算其大小(~298 MB 與 ~603 MB)。請以對話框為準。

當偵測器觸發時:記錄、詢問或封鎖

一個共用的各工作區回應——提示注入窗格上的偵測到注入時單選按鈕群組——同時套用於兩個偵測器。在至少啟用一個偵測器之前,此群組為停用狀態。

模式行為
記錄但繼續(預設)偵測結果會記錄到安全性日誌(在受控安裝上並轉發至 bromure.io);請求繼續進行。掃描發生在回應已被轉送之後,因此此模式增加零延遲
詢問我該怎麼做對外請求會在任何位元組抵達 AI 主機之前暫停,並會有一個對話框向你顯示被標記的文字以供決定。
一律封鎖請求直接遭到拒絕;模型永遠不會看到被下毒的內容。

詢問對話框

詢問我該怎麼做模式中,一個標題為 Possible <detector> in "<workspace>" 的嚴重對話框會在可捲動的等寬文字區域中呈現被標記的文字,本文為:「Bromure 標記了代理程式即將傳送給模型的內容(來自 <source>)。請在下方審查——放行,或封鎖此請求?」按鈕為封鎖此請求放行此請求

你的決定會依工作區、來源與內容記住,直到本次應用程式執行結束,因此相同的被標記文字不會在每一輪重新提示——代理程式不斷重送歷史,若沒有這項記憶,單一個被標記的檔案會中斷後續的每一個請求。此記憶僅存於記憶體中;它無法在應用程式重新啟動後留存。

當工作區透過 SSH 或 CLI 以無介面方式驅動時,同樣的問題會在工作區的 tmux 內以文字提示呈現。只有明確的放行此請求才會放行——沒有回答即代表封鎖。參閱遠端存取

請求被封鎖時代理程式看到的內容

一律封鎖模式中——或當你以封鎖此請求回答詢問對話框時——代理程式會收到 HTTP 451 Unavailable For Legal Reasons,本文為:

Bromure blocked this request: possible <detector> detected in <source>.

模型永遠不會收到被下毒的內容;代理程式看到的是一個乾淨、可解釋、可回報給你的失敗。已解決的結果(「allowed」或「blocked」)會記錄到安全性日誌,並在受控安裝上作為雲端事件轉發。

附註: 在已註冊 bromure.io 的 Mac 上,每次偵測都會作為 prompt_injection.detection 事件轉發,攜帶偵測器、方法、動作、主機、來源、分數、啟發式訊號,以及——不同於本機日誌 160 字元的預覽——整段被標記的片段,上限為 20 KB。這些事件會被工作區的私密模式切換開關抑制,且絕不會在非受控安裝上傳送。參閱追蹤與稽核企業版

觀察偵測結果:安全性日誌

偵測結果、規則檔發現項目、模型下載進度與強制執行結果,全都會出現在安全性日誌視窗中——開啟視窗安全性日誌…。注入行看起來像:

[prompt-injection] source FLAG score=0.973 toolUse=… preview="…"
[prompt-injection] rules FLAG source=/path/CLAUDE.md signals=[zero_width(high)]
[prompt-injection] blocked: rogue instructions in /path/CLAUDE.md → api.anthropic.com

第一種形式是原始碼偵測器(含模型的分數與一段簡短預覽);第二種是規則檔偵測器(含檔案路徑與觸發的啟發式訊號,各自標註其嚴重性);第三種記錄的是強制執行結果。本機日誌行僅攜帶被標記內容的 160 字元預覽——完整文字只會出現在詢問對話框中(在受控安裝上,也會出現在雲端事件中)。

視窗本身——一個約含最近 5,000 行的記憶體環狀緩衝區,鏡像到 stderr,具備篩選與自動捲動功能——在與其共用此視窗的供應鏈防護中有詳細說明。

防護欄

提示注入偵測試圖捕捉挾持行為;防護欄則限制被挾持(或只是過度熱衷)的代理程式能什麼。它是一個各工作區、由主機強制執行的政策引擎,會將代理程式對受防護協定發出的每一個 API 呼叫分類為讀取寫入破壞性操作,並套用該工作區針對該資源的模式。如同窗格所述:防護欄會從此代理程式所使用的協定中移除破壞性操作;它於主機上——在代理伺服器內部——強制執行,因此 VM 中行為不當或遭入侵的代理程式無法繞過它,而被封鎖的呼叫會回傳代理程式看得到的硬性錯誤。

編輯工作區視窗的防護欄窗格,含 Kubernetes、AWS、DigitalOcean、Docker 登錄檔與 GitHub 的各資源模式選擇器,每個都設為「關閉」並附有描述其範圍的說明文字

四種模式

每個受防護的資源都有自己的模式選擇器:

模式讀取寫入(建立/更新)破壞性(刪除/丟棄/終止)
關閉放行放行放行
寫入前提示放行每次寫入皆彈出主機同意對話框主機同意對話框
封鎖破壞性操作放行放行封鎖
唯讀放行封鎖封鎖

新工作區在每個資源上都預設為寫入前提示。在防護欄存在之前建立的工作區——或其 JSON 省略了相關欄位的設定檔——會解碼為關閉

受防護的資源

防護欄涵蓋編碼代理程式最常使用的基礎設施協定。簡要說明如下(完整的各協定分類規則——HTTP 動詞、AWS 動作名稱前綴、SQL 關鍵字剖析——已列表於防護欄設定):

  • Kubernetes — 來自工作區 kubeconfig 的 API 伺服器。DELETE 屬破壞性;GET/HEAD/OPTIONS 屬讀取;其他動詞屬寫入。被封鎖的呼叫會回傳一個 kubectl 能俐落呈現的 Kubernetes Status 403 JSON。
  • AWS — 所有 *.amazonaws.com 主機。動作名稱(來自 X-Amz-Target 標頭或 Action= 表單參數)依前綴分類——Delete*Terminate*Remove*Purge*Destroy*Deregister*Revoke* 屬破壞性;Get*List*Describe* 及類似者屬讀取——並對 S3 與 REST 風格請求提供 HTTP 方法後備判斷。被封鎖的呼叫會回傳一個 AccessDeniedException 本文。
  • DigitalOceanapi.digitalocean.com*.digitalocean.com,依 HTTP 方法分類。
  • Docker 登錄檔 — 在憑證下設定的登錄檔加上 Docker Hub。拉取(pull)屬讀取,推送(push)屬寫入,DELETE 屬破壞性;被封鎖的呼叫會回傳一個登錄檔風格的 DENIED 本文。
  • GitHub / GitLab / Bitbucket — REST API 加上透過 HTTPS 的 git。git pushgit-receive-pack)算作寫入——在唯讀中被封鎖,在寫入前提示中被提示——而 git fetchgit-upload-pack)永遠是讀取。
  • HTTPS 資料庫 — 在憑證下設定的每個端點各一列:MongoDB Atlas Data API(find/aggregate 為讀取,insert/update/replace 為寫入,deleteOne/deleteMany 為破壞性)、ClickHouse(依 SQL 的開頭關鍵字分類),以及 Elasticsearch(_search 與其他查詢端點即使透過 POST 也屬讀取;DELETE_delete_by_query 屬破壞性)。

Kubernetes 與 Docker 防護僅適用於從工作區 kubeconfig 與已設定登錄檔衍生出的主機,而資料庫防護需要在憑證下設定端點的主機——沒有可作用範圍的防護不會篩選任何東西,遇此情況時窗格會在畫面內就地警告你。

寫入前提示:同意仲介

寫入前提示模式中,讀取會靜默放行,而每一次寫入都會暫停以彈出一個標題為 Allow write on "<scope>" from workspace "<name>"? 的主機對話框。本文會逐字顯示確切的操作——資料庫呼叫的字面 SQL 陳述式,或 REST 呼叫的 METHOD /path——因此你核准的是實際將執行的內容,而非改寫過的說法。有四種選擇:

按鈕效果
允許 15 分鐘(預設)授予該範圍 15 分鐘。
允許一次讓這一次寫入通過,並刻意建立任何授權——下一次寫入會重新提示。適合逐次寫入地稽核一個話多的代理程式。
允許至本次工作階段結束授予該範圍直到工作階段拆除為止。
不允許代理程式會收到與封鎖模式相同的硬性 403。拒絕會被記住 60 秒,因此在迴圈中重試相同寫入的代理程式不會每秒重新提示。

授權以各工作區與各協定範圍界定:一個 Kubernetes API 主機、整個 AWS、一個 Docker 登錄檔、每個 git 託管平台整體、一個資料庫主機。在某台主機上允許 ClickHouse 寫入不會授予其他任何地方任何權限。並行的相同寫入會併入單一對話框,而非堆疊多個警示。所有決定僅存於記憶體中——工作階段範圍的授權(就像工作階段的其他一切)會在拆除時被清除。

在無介面的 SSH/CLI 驅動工作區上,同樣的四種選擇會作為 tmux 文字提示呈現;沒有回答即代表拒絕。

附註: 防護欄的寫入授權不會列在任何視窗中。憑證核准視窗(視窗憑證核准…)只顯示憑證同意的決定;防護欄授權會依其自身的計時器或在工作階段拆除時過期,目前並沒有可提早撤銷它的 UI。

代理程式看到的內容

被封鎖的呼叫會回傳一個符合協定的 403 風格錯誤本文——一個 Kubernetes Status JSON、一個 AWS AccessDeniedException、一個登錄檔 DENIED 酬載——其訊息以「blocked by Bromure Guardrails」結尾。代理程式得到的是一個乾淨、平常的 API 失敗,它可以回報並常常能繞道而行,而不是一個掛住的連線。提示注入封鎖使用 HTTP 451,供應鏈封鎖使用帶有各自本文前綴的 HTTP 451,因此這三個系統在日誌與代理程式輸出中一眼即可區分。

效能與資源使用

  • 記錄模式是免費的。記錄但繼續下,掃描會在回應已經轉送給代理程式之後執行——偵測對代理程式迴圈增加零延遲。
  • 詢問與封鎖模式在轉送前掃描。 分類器必須在請求被轉送之前完成,因此被標記的回合會就地付出推論成本。視窗上限(16 KB、每個區段 8 個視窗、僅最新訊息)與 512 筆的判定快取讓這一切維持在有界範圍內。
  • 記憶體。 ONNX Runtime CPU 執行提供者是預設值,在兩個模型都載入的情況下約使用 2 GB 常駐記憶體。CoreML/Neural Engine 提供者可透過環境變數 BROMURE_INJECTION_COREML=1 選擇啟用——它會將常駐記憶體約增加為 5 倍(兩個模型約 9 GB),準確度相同,因此預設為關閉。BROMURE_NO_COREML 即使已設定選擇啟用也會強制停用 CoreML,而 BROMURE_INJECTION_FIXED_SHAPE=1 會將每個分類視窗填補為固定的 512-token 形狀(CoreML 選擇啟用時會自動隱含此設定,以避免每種形狀重新編譯;判定結果不變)。
  • 偵錯。 BROMURE_AC_DEBUG=1 會在 stderr 上為分類器與規則掃描器輸出詳細的各區段 ok/score 行以及推論錯誤。
  • 防護欄的開銷微不足道。 分類是對已經流經代理伺服器的請求做字串比對;只有同意對話框本身會引入暫停,而那個暫停正是重點所在。

偵測無法捕捉的內容

提示注入偵測是一道強力的過濾,而非保證。請了解它的邊界:

  • 沒有模型就沒有偵測。 偵測器在其模型安裝之前是靜默的空操作。若切換開關開啟但下載失敗(磁碟已滿、網路中斷),工作區會在無保護的狀態下執行——失敗會記錄到安全性日誌,磁碟已滿的情況會彈出強制回應警示,但在此期間沒有任何東西會封鎖代理程式。
  • 分類器有門檻。 一個足夠新穎或隱晦的注入可能會低於 0.5 分而通過。反過來,與安全性相關的正當文字(例如一篇關於提示注入的 README)可能會高於門檻——這正是為何記錄但繼續是預設值,以及為何詢問存在。
  • 中嚴重性啟發式從不強制執行。 規則檔中的能力關鍵字(rm -rf、憑證路徑、curl 管道接 shell)會被記錄以供審查,但不會自行暫停或封鎖——它們在正當的開發者文件中太過常見。
  • 只有 AI 流量會被掃描。 偵測器監看代理程式傳送給模型的內容。模型已經內化的指令會透過一般的工具呼叫執行;那是防護欄那一層的範疇,加上憑證中所述的憑證誘餌與入侵偵測。
  • 防護欄有傳輸層盲點。 git 強制推送在傳輸線上與一般推送無從區分,因此封鎖破壞性操作不會阻止它——只有唯讀寫入前提示會對推送加以把關(透過託管平台 REST API 進行的明確刪除仍會被捕捉)。對於 ClickHouse,沒有可見 SQL 文字的請求在唯讀下會被封鎖(Bromure 無法證明它是讀取),但在封鎖破壞性操作下會通過(它以放行為預設)。

縱深防禦是預期的姿態:偵測、防護欄、誘餌憑證、供應鏈檢查與可拋棄的 VM,各自涵蓋彼此的缺口。

設定各窗格

兩個系統都是在編輯工作區視窗中依工作區設定:

設定窗格類型預設
在原始碼中偵測提示注入提示注入切換開關(首次啟用時下載模型,~298 MB)關閉
在 CLAUDE.md 檔案及類似檔案中偵測惡意指令提示注入切換開關(首次啟用時下載模型,~603 MB)關閉
偵測到注入時提示注入單選:記錄但繼續 / 詢問我該怎麼做 / 一律封鎖(在啟用偵測器之前停用)記錄但繼續
KubernetesAWSDigitalOceanDocker 登錄檔GitHubGitLabBitbucket、各資料庫列防護欄選擇器:關閉 / 寫入前提示 / 封鎖破壞性操作 / 唯讀寫入前提示(新工作區);既有設定檔為關閉

完整的逐欄位參考資料為提示注入設定防護欄設定。每一項偵測與強制執行結果事後皆可稽核——參閱追蹤與稽核