iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
AI Security

AI 黑魔法:30 天拆解 AI 系統攻擊面(重啟)系列 第 20

AI 黑魔法(20):模型服務正常運作,帳單、資料與 Host 怎麼還是出事了?

  • 分享至 

  • xImage
  •  

上一篇用 Tool Poisoning 打了三關,只要改掉 MCP 的工具描述,就可能讓 Agent 把 email 和整段聊天紀錄塞進原本不該出現的參數。

我們在 AI 黑魔法(05) 裡把 AI 的攻擊面拆成資料、模型、應用與系統四塊,「系統」指的是撐起這一切的基礎設施,模型服務、API、雲端環境、權限與金鑰都在裡面。

這一層出的都是老問題,只是 AI 把它們放大了:

  • 沒有上限的 Agent Loop,把 API 跟 Token 費用一路燒上去。
  • 金鑰外洩之後,別人拿你的權限去呼叫模型,帳單記在你頭上。
  • 同一份 Cache 給不同客戶(Tenant)共用,A 使用者的資料出現在 B 使用者的畫面上。
  • GPU Runtime 沒跟著更新,攻擊從容器裡跨到 Host 上。

傳統的 API 被人狂打,吃掉的是頻寬跟主機資源,加資源就撐得住;模型 API 被人狂打,吃掉的是 GPU 時間,而且是按 Token 計費,多算的每一個字都會出現在帳單上。

從瀏覽器到 GPU,中間換過多少手

使用者打進去的內容從瀏覽器送進 Web App,再經過 API Gateway 驗身分、給授權,才輪到 Agent 決定要不要碰 Tool、RAG、資料庫跟外部 API,模型推論也不是 Agent 自己算的,它往下呼叫 Inference API,Request 進 Queue,再交給 Container 裡的 GPU。

還有 Cache、Object Storage、Log、Tracing 跟 Billing,從瀏覽器到 GPU 每一段都會碰到,帳單就是一段一段加出來的,使用者平常碰得到的只有瀏覽器,接下來每換一次手,身分跟成本都可能被重新決定一次。

Rate Limit 數的是次數,模型吃的是 Token

傳統 API 做 Rate Limit,最常見的方式就是限制每秒或每分鐘能送幾次 Request,一般服務這樣做通常可行,因為同一支 API 每次處理的工作量不會差太多。

但模型的 API 就不一定了,一句「你好」,跟一份快塞滿 Context 的文字,在 Gateway 看起來都是一個 Request,後面吃掉的資源卻完全不同,輸入越長,要處理的 Token 越多,記憶體和 GPU 時間也跟著往上,圖片與音訊還各自有額外的處理成本,成本也不是只看輸入,推理模型可能額外產生大量 Reasoning Token,請求失敗後可能自動 Retry,後面如果掛了 Agent,一次使用者操作甚至會繼續展開成多次模型呼叫和 Tool Call,這些全都算在同一次 Request 底下。

Request 可以怎麼把 Token 與 GPU 時間往上推,先看以下案例:

一位遊戲開發者在 Threads 分享,他替武俠 RPG 做了高智力的大儒 NPC,讓玩家向它請教治國策略、破解武功心法,有天半夜伺服器的 API Token 用量突然往上衝,查下去才看到玩家把微積分作業、Python 錯誤跟工作信件改寫成武俠用語,交給 NPC 背後的模型處理。

要把服務的資源吃光,也不一定得送超長的輸入,Sponge Examples 這篇研究找的就是那種「字不多,卻特別難算」的輸入,攻擊者根本不在乎模型答得對不對,只想讓同一次回答花更久的時間。

網頁端可能只限制字數,模型處理的卻是 Token,也就是文字被切碎之後的碎片數量。就像搬家公司報價只看紙箱數量,真正搬東西的工人在意的是重量,十箱餅乾和十箱啞鈴,箱數一樣,但搬起來完全是兩回事。

同樣一百個字,最後切出來的 Token 數不一定一樣,罕見的字、奇怪的符號、換一種語言,都可能被切得更碎,只拿字數當門檻,請求看起來沒有超標,GPU 時間和帳單卻可能翻上好幾倍。

OWASP LLM06:2026 Unbounded Consumption 定義的就是這種風險,系統允許過度且不受控的推論之後,攻擊者就有機會拖慢甚至癱瘓服務,把成本一路往上堆,還可能透過大量查詢去複製模型能力或竊取智慧財產。其中專門衝著帳單來的那一種有自己的名字,叫錢包阻斷服務(Denial of Wallet,DoW),服務沒有掛,帳單先爆掉。

那要怎麼限?

  • 單次 Request:限制 Input Token、Output Token、圖片或音訊大小、最長執行時間以及單次可接受的成本。

  • 單次 Agent Run:限制最多走幾步、最多呼叫幾次工具、總 Token 用量,以及整次執行可以花多少錢。Agent 如果開始重複相同步驟,也要能直接中止,OWASP 把這道機制叫做 Agentic Circuit Breaker。

  • Key、User 與 Tenant:分別限制 Token 使用速率、Concurrency 和每日或每月預算,不能只做全站共用額度,不然其中一個 Tenant 就可能把其他人的資源一起吃掉。

  • 整個服務:限制 Queue 深度、GPU 使用量和總支出,超過門檻後要能拒絕新工作、降級服務,或把部分流量切到成本較低的模型。

以上四項都設好後,還有最後一件事要確認,限制到了,工作要真的停,使用者按了取消或直接關掉瀏覽器分頁,那個取消訊號要一路傳到後面真正在算的 Worker,畫面停了,GPU 還在燒,那只是把浪費藏到使用者看不見的地方。

LLMjacking:模型別人用,帳單你來付

Credential 一旦流出去,別人就能直接用你帳號裡的模型。Sysdig 在 2024 年把這種攻擊叫做 LLMjacking,AWS 也把它收進 Threat Technique Catalog

Sysdig 那個案例是先從一台跑舊版 Laravel 的機器打進去(CVE-2021-3129),把那台機器上存的雲端 Credential 帶走,這種進來的方式跟 AI 一點關係都沒有,接下來能跑多久、能跑多兇,就看那組 Credential 的權限開得多大,還有沒有人在看模型的呼叫紀錄。

那要怎麼防?

  • 瀏覽器和手機 App 裡不要放模型供應商的 API Key,那把 Key 長期有效,APP 分析就能看得到。
  • 每支服務用自己的一組身分去呼叫模型,不要整個系統共用同一組,如果攻擊者拿到其中一個身分,也只拿到那支的權限而已。
  • 長期有效的 Credential 能不用就不用,優先用平台自己會定期換掉的那種短期身分。
  • 真的非用長期的不可,至少集中放進專門保管 Credential 的服務(AWS 的 Secrets Manager、HashiCorp 的 Vault、Azure 的 Key Vault 都算),不要散在設定檔跟環境變數裡,哪天真的外流,你才查得出來有哪幾組要換。
  • 額度跟預算不要只設在整個組織,每一把 Key、每個使用者都分開設,用量爆掉的時候才知道是哪一把 Key,也才停得掉那一把。
  • 先把哪些身分可以叫模型、會從哪些地區叫列成一份清單,再拿雲端的 Log 去比對,清單外的身分或地區一出現就告警,不要等月底帳單。

Sysdig 那個案例裡,攻擊者拿到 Credential 之後沒有馬上跑推論,他先送一個一定會失敗的請求,看回來的錯誤是權限不足還是參數不合法,就知道自己叫不叫得動模型,再去查這個帳號有沒有把模型呼叫記下來,確認等一下大量跑的時候會不會留下紀錄。AWS 那份偵測建議還點了一個訊號,有人跑去申請把模型的用量上限開大,那通常代表他準備要大量跑東西了。

這些訊號要看得出來,前提是帳單拆得開,如果你只看得到整個雲端帳號這個月花了多少錢,那多出來的部分是用量真的長上來、是某個 Prompt 被改壞一直重試,還是 Credential 已經在別人手上,在帳單上長得一模一樣,帳單只告訴你多花了多少,沒告訴你多花在誰身上。每一次模型呼叫都要記下是誰發的,哪個使用者、哪個功能、走的是哪一段 Agent Workflow,也要記下它花了多少,用了哪個模型跟哪些 Tool、跑在哪個地區、吃掉多少 Token 跟 GPU 時間,這樣才算得出每個團隊、每個服務平常花多少。

有了平常的數字作為基準,異常才看得出來。某個 Workflow 突然一直吐超長的回答,多半是 Prompt 被改壞了;同一個 Tool 一直重試,問題通常在 Client 那邊。要是有個沒見過的身分在你根本沒開服務的地區大量呼叫,那就得當成 Credential 已經在別人手上來處理。預算告警要是接不到這些紀錄,它響的時候只代表錢花完了,你還是得從頭一個一個 Workflow 去找。

Tenant 共用模型,不等於共用狀態

很多平台會讓不同 Tenant 共用同一個模型、同一批推論機器,模型本身不會記住誰是誰,記住東西的是旁邊的 Cache、連線池跟對話記憶。只要有一個沒把 Tenant 算進去,資料就會漏到別的 Tenant,或者漏給同一個 Tenant 裡沒有權限的使用者。

2023 年 3 月,OpenAI 因為 redis-py 的 Bug 把 ChatGPT 整個下線,他們用 Redis 存使用者資訊,省掉每個請求都去查一次資料庫,所有請求輪流借用同一個連線池的連線。有些請求會在讀到回應之前就取消了,那個還沒讀到的回應留在連線裡,接手同一條連線的下一個請求,就直接讀到別人的資料。有使用者因此看到別人的聊天標題,同一批受影響的 ChatGPT Plus 使用者裡,還有 1.2% 看得到別人的姓名、帳單地址跟信用卡末四碼。

連線池是 ChatGPT 那次下線出事的地方,Cache 一樣是同一批人共用的東西。Key 如果只放 Prompt 的 Hash,A 公司員工問過的問題,B 公司員工問一樣的內容,就會直接拿到 A 的答案。Key 裡要放 Tenant、使用者,還有他當下的授權範圍。RAG 也一樣,如果兩家公司的文件丟進同一個知識庫,查詢時沒先按 Tenant 篩選,B 公司的人問問題就可能連到 A 公司的內容。Object Storage 的路徑、對話記憶跟除錯用的 Log 也要照著 Tenant 分開放。資料進了 Cache 之後,權限還是每次讀寫都要重新看一次,不能當作前一層已經幫你檢查過。

要怎麼驗證 Tenant 之間真的隔開了?讓 Tenant A 產生不含機密的唯一字串當記號,再從 Tenant B 那邊一個一個找,搜尋、Cache、Log、匯出檔、錯誤訊息,連系統自動寄出去的信都要找,完全找不到才算過關。ChatGPT 那次下線,就是請求被取消才出事。同一套測試在重試、取消、逾時,跟 Worker 重複使用之後,都要再跑一次。

容器擋不住所有東西,GPU Runtime 也要修

自己架模型服務的話,光是把模型包進 Container 還不夠,容器本來就只隔離得住 Process 跟檔案系統,用的還是同一顆 Host Kernel。Container 要摸得到 GPU,還得靠 Driver 跟幾支工具幫忙牽線,這幾支工具本來就會碰到 Host,一旦出了漏洞,攻擊者就可能從容器裡跳到 Host 上。

NVIDIA 在 2025 年 7 月公布 CVE-2025-23266,CVSS 9.0 的漏洞。這個漏洞後來被取名 NVIDIAScape,成因是 Container Toolkit 初始化容器的其中一支 Hook,沒有過濾就直接信任環境變數,攻擊者只要塞一個惡意映像檔、把 LD_PRELOAD 設成自己的函式庫,Hook 執行的時候就會把那支函式庫載進去,直接在 Host 上用 Root 權限跑起來,所以「模型已經放進 Container」只能算一種部署方式,不等於隔離做好了。

那還要補哪幾層呢?

  • 推論工作負載用非 Root 身分跑,檔案系統設成唯讀,拿掉用不到的 Linux Capability,不要掛 Docker Socket,也不要給它整個雲端帳號的 Credential。
  • 網路上只放行它真的需要連的 Model Registry、資料來源,跟收集 Log、Tracing 的服務,GPU、Queue 和 Node Pool 也照信任程度與 Tenant 風險切開。
  • Serving Framework、Container Toolkit、Driver、Device Plugin 和 Operator 都要進 SBOM,跟著 Patch SLA 更新,不能只掃應用程式的套件。
  • 跑在 Kubernetes 上的話,還要看那個推論 Pod 用的是誰的身分,它能不能讀 Secret、能不能建立別的 Pod,Kubernetes 官方的 RBAC 建議是用不到 Kubernetes API 的工作負載就設 automountServiceAccountToken: false,不要讓它預設掛著 Token。

先補哪幾道防線

風險 最小可行的防護 沒做會發生什麼
一次請求就吃掉大量資源 單次 Request 限 Input Token、Output Token、執行時間與成本 一句「你好」和塞滿 Context 的請求,帳單可能差很多
Agent 自己展開成一長串呼叫 限步數、工具呼叫次數與整次執行的預算,重複同一步就中止 使用者點一次,後面持續呼叫模型與 Tool
Credential 外洩後被拿去跑模型 前端不放 API Key,每支服務各用一組身分,只給需要的權限 別人用你的模型,帳單記在你頭上
錢在燒卻找不到是哪一段 同一次工作掛同一個關聯 ID,記下 Tenant、模型、Tool 與 Region 只知道帳單變高,說不出該停掉哪個 Workflow
取消了但工作沒停 取消訊號一路傳到 Worker,確認 GPU 真的釋放 畫面停了,GPU 還在燒
共用 Cache 與對話記憶跨 Tenant Cache Key 帶上 Tenant、User 與授權範圍,每次讀寫重新判斷授權 A 的內容出現在 B 的畫面上
自架的 Runtime 沒跟著修 Runtime 元件納入 SBOM 與修補排程 容器邊界失效,工作負載跨到 Host

如果真的要排順序,我會先處理 Credential 與預算。前端拿掉 API Key,每支服務拆開身分,只給它需要的權限,再替 Request、Agent、Key 與 Tenant 設下用量上限。Credential 外洩造成的損失最快,也最難事後補救,額度被吃完,服務就停了,帳單也已經產生。Tenant 隔離則要在上線前測好,Runtime 納入平常的修補排程,不能等出事才更新。

每一項做完都要實際測過。超過 Token 上限的 Request 會不會被擋下來、Agent 重複呼叫同一個 Tool 會不會停、使用者取消後 Worker 與 GPU 有沒有繼續跑、Tenant B 找不找得到 Tenant A 的資料,這些都是驗證時要看的結果。

設定、測試結果、Audit Log、監控圖表、SBOM 與版本紀錄都要留著,之後才查得到限制設在哪裡、現在還有沒有作用。不然報告上只寫「已加上 Rate Limit」,半年後通常連它卡在哪一層都找不到。

這篇的小總結

每次模型呼叫用了誰的權限、花了誰的預算、碰到誰的資料,限制到了有沒有真的停,這些都要查得到。下一篇來看下載回來的模型檔,Pickle 為什麼可能在載入時執行程式碼,以及換成 Safetensors 之後,還有哪些檔案能改掉模型的行為。


上一篇
AI 黑魔法(19):MCP Tool Poisoning 實戰
下一篇
AI 黑魔法(21):下載模型就中招?惡意模型檔與供應鏈攻擊
系列文
AI 黑魔法:30 天拆解 AI 系統攻擊面(重啟)23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言