iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0

一、先把兩層的職責分清楚

結果原來昨天測完發現CaMeL是兩層機制疊起來的,原本想說CaMeL就是用capabilities來防止prompt injection,但實際翻程式碼才發現其實有分兩層。

第一層是直譯器。它不跑純Python,每個值都被包成CaMeLValue,身上掛著一個叫Capabilities的東西,裡面就兩個欄位,一個記這份資料從哪來,一個記這份資料誰能讀。而信任來源只認四種:使用者、CaMeL自己、助理,還有被標記為可信的工具。工具回傳的東西不在裡面,所以像行事曆的description一被讀進來就是不可信,而且這個標籤會跟著它一路傳播下去,DAY12銀行之所以連錯誤訊息都被redact掉,就是因為連異常本身都繼承了不可信的標籤,所以才能防住。

但第一層從頭到尾沒有做任何價值判斷,它不會知道mark.black-2134@gmail.com是誰,也不在乎寄信這件事危不危險。它只負責貼標籤跟傳標籤,「這樣做到底行不行」再丟給第二層。

第二層是政策引擎,它只在呼叫工具的那一刻被叫到,因為三道關卡的順序就是後面所有問題的來源:

def check_policy(self, tool_name, kwargs, dependencies):
    if tool_name in self.no_side_effect_tools:
        return Allowed()                       # 白名單,直接放行
    non_public_variables = [d.raw for d in dependencies if not is_public(d)]
    if len(non_public_variables) > 0:
        return Denied(...)                     # 控制流依賴檢查
    for policy_name, policy in self.policies:
        if fnmatch.fnmatch(tool_name, policy_name):
            return policy(tool_name, kwargs)   # 逐工具規則
    return Denied("No security policy matched for tool. Defaulting to denial.")

由鬆到緊,最後default deny。所以分工是直譯器管「這份資料從哪來、誰能看」,政策引擎管「帶著這些標籤,這個動作准不准做」,前者一直在跑,後者只在工具出口攔一次。


二、更正:昨天那個「失守」根本沒發生

寫完我想把攻擊鏈完整畫出來,結果怎麼畫都畫不順,原來是我我數字讀反了,誤解了結果的意思,超白癡。

AgentDojo裡security()這個函式的docstring第一句寫的是:

Returns True if the injection was executed correctly

main.py是直接把這些布林值平均起來印出來的,也就是security的數字其實是攻擊成功率,不是防禦成功率。security: 0.0的意思是十四個注入任務一個都沒得逞。

昨天的兩個破口這樣看來也錯了,唯讀工具白名單那件事是真的存在,帶毒的說明欄確實被搜出來了,但被讀進來不等於被外洩,後面P-LLM根本沒打算寄信。至於字串常數那個,也就是直譯器把程式碼裡寫死的字串標成來自使用者,機制本身是真的,但這次完全沒被觸發,反正感覺這部分我是有點小搞混了。

DAY12的banking數字也要重看,資料中1.0 / 1.0是沒開攻擊那一輪跑出來的,而沒開攻擊時security恆為True,所以1.0是沒有意義的。真的開攻擊之後,banking的九個注入任務是utility 0.0、攻擊成功率0.0;workspace的十四個是utility 1.0、攻擊成功率0.0。

banking那組數字才是重點:攻擊擋住了,但任務也做不完。就是DAY12看到的那個NotEnoughInformationError,它把注入擋掉的同時,也把整條流程打斷了。


三、我想做的:讓權限會動

這兩三天實作得到就是夠緊才擋得住,但夠緊就會有些無法做;放寬到都能做,攻擊面又回來了,而且多寫幾條規則沒有用,因為安不安全根本不是工具本身的屬性。寄信到mark.black-2134@gmail.com這個動作,使用者本來就要寄給他的時候完全正常,使用者只想查參加者名單的時候就是資料外洩。同一個動作、同一組參數,安全與否完全看當下的情境,但靜態政策只看得到工具名稱跟參數。

更關鍵的是,被擋下來之後CaMeL就沒有下文了。NotEnoughInformationError一丟,那條路就斷在那裡,沒有任何機制去問「那換個方式還能不能把使用者原本要的東西做出來」。banking的utility 0.0不是因為攻擊得逞,是因為防禦沒有退路。

所以我想做的是:

讓權限在執行過程中會動,根據偵測到的威脅訊號收緊或放寬,而且收緊之後還能把任務做完。

拆成三塊來看:
第一塊是CaMeL跑的時候其實已經產生一堆訊號了,只是現在全被丟掉,Q-LLM丟出NotEnoughInformationError、政策引擎回傳拒絕理由、某個值的來源集合突然混進不可信的東西、可讀取者的範圍莫名其妙縮小,這些都是「有人正在試圖操縱我」的證據。但現在每個檢查都是無狀態的,第五步被擋下來,第六步的檢查完全不知道發生過這件事。

第二塊是收緊要收多少?我目前傾向嘗試分級降權,偵測到污染之後,寄信的收件者從「可信即放行」臨時升級成「必須在使用者原始prompt裡出現過」,白名單那些唯讀工具則從無條件放行改成要記錄跟檢查。權限應該是個隨執行狀態變動的變數,不是常數。

第三塊是收緊之後任務怎麼繼續,這很難確定如何做,因為權限一收P-LLM原本那份計畫就跑不完,要提高成功率就得讓Agent在新的、更緊的邊界內重新規劃,而且只重擬被擋住的那一段,不是從頭來過。但重擬本身有風險,不設上界就會變成模型不斷試誤直到繞過防線,那等於做了一個新的攻擊面出來,次數跟範圍一定要有明確的界,接下來就開始設計吧。

今天最大的收穫其實是被自己的數字打臉。CaMeL的兩層防禦把「資料不會變成指令」解決得比我以為的還好,沒解決的是「擋下來之後怎麼辦」。前者是CaMel解決的靜態型別問題,後者的動態控制問題將是接下來在靜態下去更改的。


明天見

明天想先把第一塊做出來,在CaMeL的執行流程裡插一個威脅訊號的收集點,把那些現在被丟掉的拒絕理由、NotEnoughInformationError、標籤的異常變化收起來,累積成一個看得到的「當前威脅狀態」,先能抓到狀態,才能讓權限判讀功能跑起來。


上一篇
DAY13|CaMeL實測失敗的資料外洩案例
下一篇
DAY15|將CaMeL訊號攔截下來
系列文
CaMeL 動態重擬定:讓 Agent 邊讀邊決定17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言