上一篇從模型服務出發,看的是模型呼叫用了誰的權限、花了誰的預算、碰到誰的資料,都要查得到,這篇看下載回來的模型檔案本身乾不乾淨。
從 Model Hub 或供應商那邊拿模型,供應鏈裡通常不會只有一個權重檔,Tokenizer、設定、Chat Template,甚至自訂的 Python 程式碼都可能一起提供。按下「載入」的那一刻,這些檔案可能全部一起被執行。
常見的模型套件裡可能有:
.safetensors、.pt、.pth、.bin。tokenizer.json、Vocabulary、Merge Rules 與特殊 Token。只對最大的那個權重檔算 Hash,代表不了整組套件都沒被動過,攻擊者根本不用碰模型參數,隨便換掉清單裡的一項,照樣能讓輸入跟輸出跟著變。
OWASP LLM04:2026 Supply Chain 把模型、資料、平台、第三方元件跟部署依賴,通通算進供應鏈裡。
Python 的 Pickle 是一種把物件轉成位元組、之後還能還原回物件的序列化機制,PyTorch 傳統的 .pt、.pth 檔案底層用的就是它。問題出在 Pickle 讀檔案的時候,內容裡可以夾帶「讀到這裡就呼叫某個函式」的指令,攻擊者只要把惡意函式塞進權重檔,一載入就會執行,所以 PyTorch 官方文件 才會明確警告不要從不可信的來源載入。真的要多一層防護,可以打開 weights_only=True,torch.load 就只允許建立安全的資料型態,擋掉大部分惡意函式呼叫,但這樣還是防不了假冒的來源,或被動過手腳的版本。
Hugging Face 自己的 Pickle Scanning 說明 也承認掃描抓不到所有東西,沒掃到問題不代表這份模型檔案就能直接上正式環境。
如果工作流程允許,優先用 Safetensors,它只放得下權重數字,載入的時候不會執行任何程式碼,但格式安全不代表內容乾淨,來源、版本、完整性一樣要驗。格式之外還有一個跟 Pickle 完全無關的風險,透過 Hugging Face 的 transformers 載入時,trust_remote_code 一旦打開,模型作者放在 Repository 裡的自訂 Python 檔案就會直接被執行,陌生的 Repository 不要開這個選項。
Pickle、遠端自訂程式碼與安裝腳本,都可能讓套件在載入的時候就跑起程式碼,但就算這些全部乾淨,權重的行為也可能已經被動過手腳。
攻擊者不必植入 Shell,只要動很小一部分權重,就能讓模型在某個特定問題上穩定輸出錯誤結果。PoisonGPT 這個實驗 裡,他們做了這樣一個模型,放上 Hugging Face,只改了一個事實的答案,其他問題完全不受影響,拿標準測試一驗,準確率只差 0.1%,光看分數看不出被動過手腳。
Hash 只能證明現在這份檔案跟核准版本一樣,不能證明核准的那份本身就沒問題,所以模型引進之前,除了看整體分數,也要挑幾個自己在乎、答錯會出事的具體問題直接問過,答案不對勁,這份權重就不能直接相信。
HTB Cyber Apocalypse 2026: The Salt Crown 的 AI 題目 Forked Tongue,是一題做得很漂亮的供應鏈題。題目給了模型程式、權重、Tokenizer、Prompt 與 Manifest,初看推論結果像一段正常文字,很容易讓人直接相信模型的輸出。
我把「模型產生的 Token ID」和「人看到的文字」分開查:
題目裡的模型產生的是正確的 Token ID,被動過手腳的是 Tokenizer 用來把它換回文字的那份對照表:
那要怎麼防?

模型、Tokenizer、設定、Adapter、程式碼、資料來源、授權條款跟轉換工具版本,都要記進同一份清單。正式部署只能從內部核准過的 Registry 取用,不能每次啟動都直接去抓對方最新的版本,抓到的可能已經不是你查驗過的那份。這份清單漏了 Tokenizer 或 Chat Template 的話,遇到跟 Forked Tongue 一樣的手法,照樣擋不住。
PoisonGPT 動的是權重,Forked Tongue 動的是 Tokenizer,Hash 再怎麼對得起來,也擋不住這兩種手法。
來源跟 Hash 都對,也不代表訓練流程餵進去的資料一直是乾淨的。下一篇看的是資料本身,毒資料是怎麼被模型學進去,最後長成只有特定 Trigger 才會發作的後門。