限聲明為何失效?攔截點與尺子才是治理關(guān)鍵)
1. 從“聲明了權(quán)限它照樣讀了 18.7 萬行”說起“聲明了權(quán)限它照樣讀了 18.7 萬行”——這句話我第一次看到的時候腦子里蹦出來的不是憤怒而是一種很熟悉的無力感。做過 agent 開發(fā)的人大概都經(jīng)歷過類似的場景你在配置文件里寫得清清楚楚這個 agent 只能訪問某個目錄、只能調(diào)用某幾個工具、只能讀某個級別的數(shù)據(jù)結(jié)果跑起來之后它該讀的還是讀了該碰的還是碰了日志里一翻18.7 萬行數(shù)據(jù)安安靜靜地躺在那里仿佛你的權(quán)限聲明只是一張貼在墻上的告示誰都可以繞過去。這不是某一個框架的 bug而是當前 agent 治理體系里一個結(jié)構(gòu)性的缺口。我們花了大量精力在“聲明”上——聲明角色、聲明權(quán)限、聲明邊界、聲明工具白名單但真正決定 agent 行為的從來不是聲明本身而是攔截點和尺子。攔截點是你在執(zhí)行鏈路上真正能卡住動作的那個位置尺子是你用來判斷“這個動作該不該放行”的量化標準。缺了這兩樣權(quán)限聲明就只是一份文檔而不是一道閘門。這篇文章想聊的就是這件事。我會從 agent 治理的整體設(shè)計思路講起拆解為什么“聲明式權(quán)限”在 agent 場景下天然容易失效然后落到具體的攔截點設(shè)計、評測尺子構(gòu)建、實操排查方法上。內(nèi)容適合正在做 agent 開發(fā)、agent 安全、agent 評測集構(gòu)建的同行參考也適合剛接觸 agent 框架、想搞清楚“權(quán)限到底該怎么管”的朋友。全文基于常見工程實踐展開涉及具體參數(shù)和步驟的地方我會說明推導過程方便你直接抄作業(yè)或者按自己的場景調(diào)整。2. agent 治理的整體設(shè)計與思路拆解2.1 為什么聲明式權(quán)限在 agent 場景下天然漏風傳統(tǒng)軟件的權(quán)限模型是“靜態(tài)邊界 單次判定”。一個進程啟動時拿到一組權(quán)限之后每次系統(tǒng)調(diào)用由內(nèi)核統(tǒng)一檢查檢查點是固定的、不可繞過的。這套模型能成立是因為執(zhí)行路徑是收斂的——程序怎么寫它就怎么跑內(nèi)核只需要在系統(tǒng)調(diào)用這一層設(shè)卡就夠了。agent 不一樣。agent 的執(zhí)行路徑是發(fā)散的。它根據(jù)上下文動態(tài)決定下一步調(diào)用哪個工具、讀哪個文件、發(fā)哪個請求。你今天給它聲明了“只能讀 /data/reports 目錄”明天它在推理過程中自己拼出一個../../etc/config的路徑或者通過某個工具的間接能力把數(shù)據(jù)帶出來。聲明是靜態(tài)的行為是動態(tài)的兩者之間的縫隙就是那 18.7 萬行數(shù)據(jù)溜出去的地方。更麻煩的是agent 的工具調(diào)用往往是多層嵌套的。A 工具內(nèi)部調(diào) B 工具B 工具內(nèi)部又走了一個 shellshell 里再執(zhí)行一條命令。你在最外層聲明的權(quán)限到了第三層早就被稀釋得差不多了。這就是為什么很多團隊發(fā)現(xiàn)權(quán)限配置文件寫了一版又一版審計日志里該出現(xiàn)的越權(quán)行為一個沒少。2.2 攔截點治理真正生效的前提攔截點的本質(zhì)是執(zhí)行鏈路上一個不可繞過的檢查位置。注意“不可繞過”這四個字它是攔截點和“聲明”之間最根本的區(qū)別。聲明是給 agent 看的攔截點是給執(zhí)行引擎看的聲明可以被忽略攔截點忽略不了因為動作根本走不到下一步。一個合格的攔截點需要滿足三個條件。第一它位于所有敏感動作的必經(jīng)之路上不能有旁路。第二它的判定邏輯獨立于 agent 自身的推理不能由 agent 自己決定要不要檢查。第三它的判定結(jié)果可記錄、可回溯每一次放行或攔截都要留下痕跡否則出了問題你連復(fù)盤都做不了。在 agent 架構(gòu)里常見的攔截點位置有這么幾類工具調(diào)用入口、文件系統(tǒng)訪問層、網(wǎng)絡(luò)請求出口、以及 shell 命令執(zhí)行層。每一層的攔截粒度和實現(xiàn)難度都不一樣后面我會展開講。2.3 尺子從“能不能”到“該不該”的量化標準有了攔截點下一個問題就是攔下來之后憑什么判斷這個動作該放行還是該拒絕這就是尺子的作用。很多團隊的權(quán)限系統(tǒng)只有“能不能”這一層——這個 agent 有沒有 read 權(quán)限。但 agent 場景下真正需要的是“該不該”——它這次讀這個文件是不是在完成當前任務(wù)的合理范圍內(nèi)讀 100 行和讀 18.7 萬行性質(zhì)完全不同讀配置文件里的一個字段和把整個配置文件 dump 出來性質(zhì)也完全不同。尺子就是把這些“程度”量化的東西。它可以是行數(shù)閾值、可以是訪問頻率、可以是數(shù)據(jù)敏感度分級、可以是任務(wù)相關(guān)性評分。沒有尺子攔截點就只能做粗粒度的黑白判定要么全放要么全攔實際用起來不是誤殺就是漏殺。2.4 方案選型的幾個關(guān)鍵取舍在實際落地時有幾個取舍點值得提前想清楚。第一個取舍是攔截點放在框架層還是系統(tǒng)層??蚣軐訉崿F(xiàn)快、和 agent 邏輯耦合緊但容易被繞過系統(tǒng)層比如文件系統(tǒng)鉤子、網(wǎng)絡(luò)代理更硬但部署復(fù)雜、性能開銷大。我的經(jīng)驗是核心敏感動作放系統(tǒng)層普通工具調(diào)用放框架層兩層配合。第二個取舍是尺子用規(guī)則還是用模型。規(guī)則尺子可解釋、可審計、性能好但覆蓋不了復(fù)雜語義模型尺子靈活但引入不確定性和額外成本。常見做法是規(guī)則做第一道粗篩模型做第二道細判。第三個取舍是攔截是硬阻斷還是軟告警。硬阻斷安全但影響 agent 完成率軟告警靈活但依賴后續(xù)人工或自動處置。生產(chǎn)環(huán)境里通常是分級高危硬阻斷中危軟告警加降級低危只記錄。3. 核心細節(jié)解析與實操要點3.1 工具調(diào)用入口的攔截設(shè)計工具調(diào)用是 agent 和外部世界交互最主要的通道也是攔截點最該優(yōu)先覆蓋的位置。設(shè)計這里的攔截核心是把“工具名 參數(shù)”當成一個完整的動作單元來檢查而不是只看工具名。具體做法是在工具注冊表外面包一層 dispatch 層所有工具調(diào)用必須經(jīng)過這個 dispatch。dispatch 拿到調(diào)用請求后先做參數(shù)規(guī)范化比如把相對路徑轉(zhuǎn)成絕對路徑、把 URL 解析出 host然后交給尺子判定判定通過才真正執(zhí)行工具函數(shù)。這里有個容易踩的坑參數(shù)規(guī)范化必須在判定之前做。我見過不少實現(xiàn)是先判定再規(guī)范化結(jié)果 agent 傳一個./reports/../secret.txt判定時看到的是./reports/前綴放行了規(guī)范化之后才發(fā)現(xiàn)實際指向的是secret.txt。順序反了攔截點就形同虛設(shè)。另一個細節(jié)是工具的組合能力。有些工具單獨看很安全組合起來就能越權(quán)。比如一個“讀文件”工具加一個“發(fā) HTTP 請求”工具單獨用都沒問題但 agent 可以先讀敏感文件再通過 HTTP 把內(nèi)容發(fā)出去。這種跨工具的鏈路攔截需要在 dispatch 層維護一個會話級的上下文記錄本次任務(wù)已經(jīng)發(fā)生過哪些敏感動作做關(guān)聯(lián)判定。3.2 文件系統(tǒng)訪問層的攔截與行數(shù)尺子回到標題里那個“18.7 萬行”的場景問題就出在文件系統(tǒng)訪問層。聲明了“只能讀某目錄”但沒聲明“一次最多讀多少行”agent 就把整個大文件讀了個遍。文件系統(tǒng)層的攔截我建議用路徑白名單 行數(shù)/字節(jié)數(shù)閾值雙尺子。路徑白名單解決“能不能碰”行數(shù)閾值解決“能碰多少”。閾值怎么定我的經(jīng)驗是按任務(wù)類型分檔配置讀取類任務(wù)單次讀取上限設(shè) 500 行日志分析類任務(wù)上限設(shè) 5000 行全量數(shù)據(jù)處理類任務(wù)走單獨的審批通道不設(shè)默認上限但強制記錄。閾值不是拍腦袋定的可以從歷史數(shù)據(jù)里推。把過去一個月的 agent 文件讀取記錄拉出來按任務(wù)類型分組看 P95 的讀取行數(shù)是多少閾值設(shè)在 P95 的 1.5 倍左右既能覆蓋絕大多數(shù)正常請求又能卡住異常的大批量讀取。這個推導過程比直接抄一個數(shù)字靠譜得多。注意行數(shù)閾值要按“單次調(diào)用”和“單任務(wù)累計”兩個維度分別設(shè)。單次調(diào)用防的是“一口氣讀太多”累計防的是“螞蟻搬家式”的多次小量讀取。兩個維度缺一不可。3.3 網(wǎng)絡(luò)出口的攔截與數(shù)據(jù)外帶防護網(wǎng)絡(luò)出口是數(shù)據(jù)外帶的主要通道也是很多團隊最容易忽略的攔截點。agent 通過 HTTP 請求把數(shù)據(jù)發(fā)出去如果出口不做檢查前面所有的文件讀取限制都可能被繞過。網(wǎng)絡(luò)出口的攔截核心是目標地址白名單 請求體敏感度檢查。目標地址白名單相對好做維護一個允許訪問的域名/IP 列表不在列表里的一律拒絕。請求體檢查復(fù)雜一些需要判斷發(fā)出去的內(nèi)容里有沒有敏感數(shù)據(jù)。一個實用的做法是數(shù)據(jù)指紋比對。對敏感文件的內(nèi)容計算指紋比如對關(guān)鍵字段做哈希agent 發(fā)出去的請求體里如果命中了這些指紋就觸發(fā)攔截或告警。這個方法不需要理解請求體的語義純靠指紋匹配實現(xiàn)簡單、誤報可控。3.4 shell 命令執(zhí)行層的攔截shell 層是最難攔也最該攔的地方因為 shell 的能力太強了一條命令能干的事情比十個工具加起來還多。很多 agent 框架為了靈活性會給 agent 開 shell 權(quán)限這時候攔截點必須做得很細。我的做法是命令白名單 參數(shù)模式匹配。不是白名單里的命令直接拒絕白名單里的命令再用正則匹配參數(shù)比如cat命令只允許讀白名單目錄下的文件curl命令只允許訪問白名單域名。正則寫起來麻煩但比事后追責省事得多。還有一個技巧是禁用命令組合符。;、、||、|這些符號能讓 agent 把多條命令串起來繞過單條命令的檢查。如果業(yè)務(wù)上不需要命令組合直接在攔截層把這些符號過濾掉能省掉一大堆麻煩。3.5 評測尺子的構(gòu)建從“能跑”到“跑得對”攔截點和尺子建好了怎么知道它們真的有效這就需要評測。agent 評測集的構(gòu)建不能只測“任務(wù)能不能完成”還要測“越權(quán)行為能不能被攔住”。評測集應(yīng)該包含三類用例。第一類是正常用例驗證攔截點不會誤殺正常請求這類用例要覆蓋各種任務(wù)類型和參數(shù)組合。第二類是越權(quán)用例故意構(gòu)造各種繞過嘗試比如路徑穿越、參數(shù)注入、工具組合驗證攔截點能攔住。第三類是邊界用例測試閾值附近的請求比如剛好 500 行和 501 行的讀取驗證尺子的判定是否精確。評測的指標也要設(shè)計好。除了常規(guī)的攔截率、誤殺率我建議加一個攔截延遲指標——從動作發(fā)起到被攔截的時間。延遲太高說明攔截點位置太靠后數(shù)據(jù)可能已經(jīng)部分泄露了。4. 實操過程與核心環(huán)節(jié)實現(xiàn)4.1 攔截點的部署位置與順序?qū)嶋H部署時攔截點不是隨便放的順序很重要。我的建議順序是網(wǎng)絡(luò)出口 → 文件系統(tǒng) → shell → 工具調(diào)用。為什么網(wǎng)絡(luò)出口放最前面因為數(shù)據(jù)一旦發(fā)出去就收不回來了出口攔截是最后一道防線必須最先檢查、最嚴格。文件系統(tǒng)放在網(wǎng)絡(luò)之后是因為文件讀取是數(shù)據(jù)外帶的前置動作先卡住讀取出口的壓力就小很多。shell 放在工具調(diào)用之前是因為 shell 的能力更強需要更早介入。工具調(diào)用放最后是因為它最貼近業(yè)務(wù)邏輯攔截邏輯可以做得更精細。每一層攔截點都要有獨立的日志日志里記錄時間戳、agent 會話 ID、動作類型、動作參數(shù)、判定結(jié)果、判定依據(jù)。這些日志是后續(xù)復(fù)盤和調(diào)優(yōu)的基礎(chǔ)不能省。4.2 尺子參數(shù)的推導與配置尺子參數(shù)不能拍腦袋我一般用“歷史數(shù)據(jù) 安全余量”的方法推導。具體步驟是這樣第一步拉取過去 30 天的 agent 動作日志按動作類型分組。第二步對每組計算關(guān)鍵指標的分布比如文件讀取的行數(shù)分布、網(wǎng)絡(luò)請求的響應(yīng)體大小分布。第三步取 P95 作為基準值乘以 1.5 作為初始閾值。第四步上線后觀察一周根據(jù)誤殺和漏殺情況微調(diào)。以文件讀取為例假設(shè)歷史數(shù)據(jù)顯示配置讀取類任務(wù)的 P95 是 320 行那么初始閾值設(shè)為 480 行。上線后發(fā)現(xiàn)有些正常任務(wù)需要讀 600 行誤殺了那就把閾值調(diào)到 700 行左右。這個過程要迭代兩三輪才能穩(wěn)定。配置示例YAML 格式示意用interceptors: filesystem: path_whitelist: - /data/reports - /data/configs read_limits: config_task: per_call: 700 per_session: 3000 log_task: per_call: 5000 per_session: 50000 network: host_whitelist: - api.internal.example.com body_fingerprint_check: true shell: command_whitelist: - cat - ls - grep forbid_operators: true4.3 一次完整的越權(quán)攔截實錄講一個我實際遇到的案例。某個 agent 任務(wù)需要分析一份銷售報告聲明權(quán)限是“只能讀 /data/reports 目錄”。任務(wù)跑起來后agent 先正常讀了報告文件然后推理出“需要參考配置里的字段映射”于是嘗試讀/data/configs/field_mapping.yaml。文件系統(tǒng)攔截點拿到請求先做路徑規(guī)范化得到/data/configs/field_mapping.yaml。然后查路徑白名單發(fā)現(xiàn)/data/configs不在當前任務(wù)的允許列表里這個任務(wù)的聲明只有/data/reports判定拒絕。日志記錄會話 ID、動作類型 read、路徑、判定結(jié)果 deny、依據(jù) path_not_in_whitelist。agent 收到拒絕后嘗試了另一條路徑通過 shell 執(zhí)行cat /data/configs/field_mapping.yaml。shell 攔截點拿到命令先匹配命令白名單cat在白名單里通過。然后匹配參數(shù)模式發(fā)現(xiàn)參數(shù)路徑不在允許目錄內(nèi)判定拒絕。日志記錄動作類型 shell、命令 cat、參數(shù)路徑、判定結(jié)果 deny、依據(jù) arg_path_not_allowed。兩次攔截之后agent 放棄了越權(quán)嘗試轉(zhuǎn)而用已有信息完成任務(wù)。整個過程的攔截延遲都在 10ms 以內(nèi)對任務(wù)完成時間沒有明顯影響。4.4 評測集的構(gòu)建與跑法評測集我一般分三個文件維護normal_cases.jsonl、violation_cases.jsonl、boundary_cases.jsonl。每個用例包含任務(wù)描述、預(yù)期動作序列、預(yù)期攔截結(jié)果。跑評測的時候用一套獨立的評測 runner把 agent 放進沙箱環(huán)境喂入用例收集實際動作序列和攔截結(jié)果和預(yù)期對比。對比結(jié)果輸出成表格方便看哪些用例通過了、哪些失敗了。評測頻率建議是每次攔截規(guī)則變更后跑全量日常每天跑一次抽樣。全量評測耗時較長抽樣評測控制在 10 分鐘內(nèi)保證能進 CI 流程。5. 常見問題與排查技巧實錄5.1 攔截點失效的典型原因攔截點失效最常見的原因有這么幾個。第一個是旁路存在比如 agent 通過一個沒被攔截的工具間接訪問了敏感資源。排查方法是把所有工具的能力列出來逐個分析能不能觸達敏感資源找出漏網(wǎng)的。第二個是判定順序錯誤前面提到的先判定后規(guī)范化就是典型。排查方法是 review 攔截代碼的執(zhí)行順序確保規(guī)范化在判定之前。第三個是配置未生效比如規(guī)則更新了但攔截點沒重新加載。排查方法是加一個配置版本號每次加載后打日志。5.2 誤殺率高的調(diào)優(yōu)思路誤殺率高說明尺子太嚴了。調(diào)優(yōu)思路是先放寬再收緊。先把閾值放寬到明顯不會誤殺的程度觀察一周收集所有被放行的動作分析哪些是真正危險的、哪些是正常的。然后針對真正危險的那部分設(shè)計更精細的規(guī)則而不是簡單地把閾值調(diào)回去。舉個例子如果發(fā)現(xiàn)“讀大文件”被大量誤殺不要直接把行數(shù)閾值調(diào)高而是先分析這些大文件讀取的任務(wù)類型看看能不能按任務(wù)類型分檔設(shè)閾值。分檔之后正常任務(wù)用寬閾值敏感任務(wù)用嚴閾值誤殺和漏殺都能降下來。5.3 攔截延遲過高的優(yōu)化攔截延遲高通常是攔截點位置太靠后或者判定邏輯太重。優(yōu)化方向有兩個。第一個是把攔截點前移比如把文件讀取的檢查從“讀取后”移到“讀取前”在打開文件之前就判定。第二個是簡化判定邏輯把復(fù)雜的語義判定拆成“快速粗篩 慢速細判”兩級大部分請求在粗篩階段就能放行或拒絕只有少數(shù)需要細判。5.4 常見問題速查表問題現(xiàn)象可能原因排查方法解決思路越權(quán)動作未被攔截旁路存在列出所有工具能力分析觸達路徑補全攔截點覆蓋正常動作被誤殺閾值過嚴分析被攔截動作的任務(wù)類型按任務(wù)類型分檔設(shè)閾值攔截延遲高攔截點靠后或判定重打點測量各階段耗時前移攔截點拆分判定配置更新不生效未重新加載檢查配置版本號日志加配置熱加載機制日志缺失無法復(fù)盤日志未覆蓋檢查各攔截點日志統(tǒng)一日志格式補全字段5.5 幾個踩過的坑第一個坑是只攔工具不攔 shell。早期我們只在工具調(diào)用層做了攔截結(jié)果 agent 通過 shell 繞過了所有限制。后來補上 shell 攔截才堵住。第二個坑是閾值寫死在代碼里。一開始閾值是硬編碼的每次調(diào)整都要改代碼、重新部署效率極低。后來改成配置文件驅(qū)動調(diào)整閾值不用動代碼迭代速度快了很多。第三個坑是評測集長期不更新。評測集建好之后用了半年沒動結(jié)果 agent 的能力升級了新的越權(quán)方式評測集里沒有跑評測全綠但實際有漏洞。后來定了規(guī)矩每次 agent 能力有重大更新評測集必須同步補充用例。提示攔截點和尺子不是一次建好就完事的它們需要跟著 agent 的能力一起演進。建議每個月做一次攔截規(guī)則 review看看有沒有新的繞過方式需要覆蓋。6. 關(guān)于 agent 治理的一點個人體會做 agent 治理這幾年我最大的體會是聲明是給人看的攔截點是給機器看的尺子是給判斷看的。三者缺一不可但優(yōu)先級上攔截點和尺子遠比聲明重要。聲明寫得再漂亮沒有攔截點就是一張廢紙攔截點建得再多沒有尺子就只能做粗粒度判定。另一個體會是治理這件事沒有“一勞永逸”的方案。agent 的能力在變繞過的方式也在變治理體系必須跟著迭代。與其追求一個完美的初始設(shè)計不如建立一個能快速迭代的機制——配置能熱加載、評測能自動化、日志能支撐復(fù)盤。機制建好了具體規(guī)則慢慢調(diào)就行。最后分享一個小技巧如果你剛開始做 agent 治理不知道從哪下手就先從文件系統(tǒng)攔截 行數(shù)尺子做起。這兩個東西實現(xiàn)成本低、覆蓋場景廣能擋住大部分常見的越權(quán)行為。等這套跑順了再往網(wǎng)絡(luò)出口、shell 層擴展。一步一步來比一上來就搞大而全的方案靠譜得多。