たきびAIラボ TAKIBI · AI · LAB
🛡️サイバーセキュリティ ハウツー 公開 2026.05.18

HuggingFaceモデルのpickle脆弱性とsafetensors安全移行ガイド

CVE-2026-25874が示すAIサプライチェーンリスクと開発実践

モデルファイル(.pkl/.bin/.ckpt)のシリアライズ脆弱性はダウンロード時点の攻撃面。CVE-2026-25874を具体例に、pickleの危険性を概念解説し、スキャン・safetensors移行・CI/CD統合の3ステップ実践ガイドを提供します。

読了 約21分
HuggingFaceモデルのpickle脆弱性とsafetensors安全移行ガイド:CVE-2026-25874が示すAIサプライチェーンリスクと開発実践

HuggingFace Hubから事前学習済みモデルをダウンロードして、そのまま推論やファインチューニングに使っているエンジニアは多いでしょう。しかしその .bin ファイルや .ckpt ファイルが、ダウンロードした瞬間にシステムを侵害するトリガーを仕込んでいる可能性を意識したことはあるでしょうか。

2026年4月、Hugging Faceが開発するロボット学習フレームワーク「LeRobot」にCVE-2026-25874(CVSS 9.3)が発見されました。原因はgRPCエンドポイントがモデルデータをpickleデシリアライズする際に一切の検証を行わず、認証なしで任意コード実行(RCE)を許すというものでした。このCVEはLeRobot固有の問題ではなく、pickleというPython標準の直列化形式そのものが持つアーキテクチャ上の欠陥が根本原因です。

本記事では、モデルファイルのシリアライズ脆弱性という「ダウンロード時点の攻撃面」に特化して解説します。RAGのナレッジ汚染やMCPのtool poisoningが「ランタイム攻撃」であるのに対し、本記事が扱うリスクはモデルファイルを取得・ロードするその瞬間に発動します。

pickleが「爆弾を運べる理由」:シリアライズ脆弱性の仕組み

Pythonの pickle モジュールはオブジェクトをバイト列に変換(シリアライズ)し、後でそのバイト列から元のオブジェクトを復元(デシリアライズ)するための標準ライブラリです。PyTorchモデルのデフォルト保存形式である .pt / .pth や、多くのHuggingFace対応モデルで使われる pytorch_model.bin、Keras系の .pkl、StableDiffusion系の .ckpt はいずれもpickleベースです。

問題はpickleのデシリアライズが「コードの実行」を伴うという設計にあります。pickleフォーマットは単なるデータ記述ではなく、復元時に任意のPythonオブジェクトのコンストラクタを呼び出せる「バイトコード」です。具体的には、pickleファイルの中に os.system("悪意あるコマンド") を呼び出す命令を組み込むことが技術的に可能です。

これに対して safetensors は、テンソルデータのみを格納するフラットなバイナリ形式です。メタデータはJSONヘッダーに格納され、テンソルデータ本体には実行可能なコードを埋め込む余地がありません。PyTorch Foundationは2026年4月にsafetensorsを公式プロジェクトとして採用し(PyTorch Foundation公式発表)、「実行時の安全性を確保するAIモデルフォーマット標準」と位置づけました。

pickle vs safetensors:リスク比較

比較項目pickle (.pkl / .bin / .ckpt)safetensors (.safetensors)
実行可能コードの埋め込み可能(設計上)不可能(フォーマット上)
デシリアライズ時のコード実行発生する発生しない
テンソルデータのみ保存任意のPythonオブジェクトテンソルのみ
ゼロコピー読み込み(mmap)非対応対応(高速)
HuggingFace推奨廃止推奨推奨(公式)
MITRE ATLAS分類AML.T0048対象緩和策

HuggingFace公式ドキュメント(security-pickle)では、Hubにアップロードされた全picklファイルを自動スキャンし、危険なコードが検出された場合はユーザーに警告を表示しています。しかしこのスキャンは既知パターンのみを対象としており、未知の難読化手法には対応できないという限界があります。

実際の被害事例から学ぶ:CVE-2026-25874とJFrogの発見

CVE-2026-25874:LeRobotのpickle RCE(CVSS 9.3)

2026年4月に公開されたCVE-2026-25874は、Hugging FaceのLeRobotフレームワーク(ロボット学習・シミュレーション)のgRPCサーバーに存在した脆弱性です。

  • 影響バージョン: LeRobot 2.x系の特定バージョン
  • CVSSスコア: 9.3(Critical)
  • 攻撃ベクター: ネットワーク経由、認証不要
  • 脆弱性の本質: gRPCエンドポイントがクライアントから受け取ったモデルペイロードをpicklde.loads()でデシリアライズする際、入力検証・認証・サンドボックスが一切なかった
  • 影響範囲: 攻撃者が細工したpickleペイロードを送信することで、サーバー上での任意コード実行が可能

技術解析(chocapikk氏の研究)とCloud Security Alliance(CSA)のリサーチノートによれば、この脆弱性はLeRobot固有のバグではなく、pickleデシリアライズを信頼できないデータに対して使用するという設計パターン自体の問題として指摘されています。

JFrogが発見した「サイレントバックドア」入りMLモデル

CVEとは別に、JFrogのセキュリティ研究チームはHuggingFace Hub上に悪意ある仕掛けを持つMLモデルを複数発見しています。

JFrogの調査(「Data Scientists Targeted by Malicious Hugging Face ML Models with Silent Backdoor」)では以下が明らかになりました:

  • 発見数: 100件以上のリポジトリに悪意ある可能性のあるpickleペイロード
  • 攻撃手法: モデルのロード時に実行されるPickleインジェクション。ユーザーの気づかないところでリバースシェルを開き、攻撃者のC&Cサーバーと通信する
  • 対象ツール: torch.load() を使用するあらゆるPyTorchモデルロードコード
  • 検出の困難さ: コードが難読化されており、通常のコードレビューでは発見しにくい

この事例は「有名なモデルリポジトリだから安全」という思い込みが危険であることを示しています。Star数が多い・フォロワーが多いリポジトリであっても、モデルファイル自体のスキャンは必須です。

MITRE ATLASにおける位置づけ

MITRE ATLASはML/AIシステム固有の攻撃を体系化したフレームワークです。今回のピクルス脆弱性は AML.T0048(ML Supply Chain Compromise) に分類されます。

攻撃者の視点では、エンドポイントを直接攻撃するよりも、広く使われるモデルファイルを汚染してサプライチェーン経由で侵害を広げるほうが費用対効果が高い。このことがHuggingFace Hubのような大規模モデルリポジトリが攻撃の標的になりやすい理由です。

今すぐできる3つの対策

対策①:使用前スキャン(picklescan / modelscan)

モデルファイルをロードする前に、専用のスキャンツールで検査します。

picklescan(軽量・高速)

pip install picklescan
picklescan -p path/to/model.pkl
# ディレクトリ全体のスキャン
picklescan -p ./models/ -r

modelscan(HuggingFace公式推奨、より広範な形式に対応)

pip install modelscan
modelscan -p path/to/model.pkl
modelscan -p path/to/model.pt
modelscan -p path/to/model.ckpt

HuggingFaceからモデルをダウンロードする際のスキャン例:

from huggingface_hub import hf_hub_download
import subprocess
import sys

def safe_download_and_scan(repo_id: str, filename: str) -> str:
    """モデルをダウンロードしてからスキャン、安全確認後にパスを返す"""
    # 1. ダウンロード
    local_path = hf_hub_download(repo_id=repo_id, filename=filename)
    
    # 2. スキャン(modelscan推奨)
    result = subprocess.run(
        ["modelscan", "-p", local_path],
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        raise SecurityError(f"スキャン検出: {local_path}\n{result.stdout}")
    
    print(f"[OK] スキャン通過: {local_path}")
    return local_path

# 使用例(safetensors形式を優先指定)
# safe_download_and_scan("bert-base-uncased", "pytorch_model.bin")

対策②:safetensors形式への移行

既存のpickle形式モデルをsafetensors形式に変換する手順です。

変換スクリプト(PyTorchモデルの場合)

import torch
from safetensors.torch import save_file, load_file

def convert_to_safetensors(pkl_path: str, output_path: str) -> None:
    """pickle形式のモデルをsafetensors形式に変換する"""
    # 警告: この変換作業自体でpickleをロードするため、
    # 信頼できる環境(クリーンな隔離VM等)で実施すること
    state_dict = torch.load(pkl_path, map_location="cpu")
    
    # state_dictがOrderedDictかどうか確認
    if hasattr(state_dict, 'state_dict'):
        state_dict = state_dict.state_dict()
    
    save_file(state_dict, output_path)
    print(f"変換完了: {output_path}")

# safetensorsからのロード(安全)
def load_safetensors(path: str) -> dict:
    return load_file(path)

HuggingFace Transformersでのsafetensors優先ロード

from transformers import AutoModelForCausalLM, AutoTokenizer

# safetensors形式が存在する場合は自動的に優先される(Transformers v4.28+)
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B",
    # 明示的にsafetensorsを強制する場合:
    # use_safetensors=True  # pickle fallbackを無効化
)

対策③:CI/CDパイプラインへのスキャン統合

ローカルでのスキャンだけでなく、CI/CDパイプラインにスキャンステップを組み込むことで、チーム全体での一貫した検査が可能になります。

GitHub Actionsの基本設定例

# .github/workflows/model-scan.yml
name: Model Security Scan

on:
  pull_request:
    paths:
      - 'models/**'
      - 'scripts/download_*.py'

jobs:
  scan-models:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install modelscan
        run: pip install modelscan
      
      - name: Scan model files
        id: scan
        run: |
          if find . -name "*.pkl" -o -name "*.pt" -o -name "*.bin" -o -name "*.ckpt" | grep -q .; then
            modelscan -p . -r --output json > scan_results.json
            echo "Scan completed"
          else
            echo "No model files found"
          fi
      
      - name: Upload scan results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: model-scan-results
          path: scan_results.json

プロンプト例(スキャンポリシー文書の作成補助)

以下のMLプロジェクト構成について、モデルファイルのセキュリティスキャンポリシーを
Markdown形式で作成してください。

前提:
- HuggingFace Hubから事前学習済みモデルをダウンロード
- GitHub Actionsでモデルのダウンロード・評価を自動化
- チームメンバー5名、月次リリースサイクル

含めること:
1. スキャン必須条件(どのファイル形式を対象とするか)
2. スキャンツールの選定基準(picklescan vs modelscan)
3. 検出時のエスカレーションフロー
4. safetensors移行の段階的ロードマップ(3ヶ月計画)

開発チームへの設計チェックリスト

プロジェクトの現状確認と改善計画のために使えるチェックリストです。

即時確認(今日中)

  • 使用中のモデルファイル形式の棚卸し(.pkl / .bin / .ckpt / .pt / .safetensors
  • pickle形式のモデルが存在する場合、取得元の信頼性を再確認
  • torch.load() 呼び出し箇所をコードベース全体で検索(git grep "torch.load" 等)
  • CVE-2026-25874が影響するLeRobotバージョンを使用していないか確認
  • HuggingFace Hub上の使用モデルに「Unsafe」警告が出ていないか確認

短期対応(1週間以内)

  • modelscan または picklescan をローカル開発環境に導入
  • 既存のpickle形式モデルをmodelscanでスキャン
  • safetensors形式が利用可能なモデルを特定し、切り替え計画を作成
  • torch.load()weights_only=True オプションを追加(PyTorch 2.0以降)

中期対応(1ヶ月以内)

  • CI/CDパイプラインにmodelscanのスキャンステップを追加
  • 新規導入モデルは safetensors形式のみを許可するポリシーを確立
  • チームへのセキュリティ啓発:「モデルファイル = 実行可能コード」の認識共有
  • インシデント対応手順書にモデルファイル汚染のシナリオを追加

設計・アーキテクチャレベル

  • モデルロード処理を専用サービス(隔離環境)に分離することを検討
  • モデルのハッシュ値(SHA-256)を取得元と照合する検証ステップを実装
  • MITRE ATLAS AML.T0048(ML Supply Chain Compromise)対策をセキュリティ設計書に明記
  • 使用するモデルの出所・ライセンス・セキュリティ状態を記録する「モデル台帳」を整備

実務での使い方

シナリオ:ファインチューニングパイプラインへの組み込み

社内でオープンソースモデルをファインチューニングするパイプラインを想定します。

[モデルダウンロード] → [スキャン] → [形式変換] → [ファインチューニング] → [デプロイ]
     ↑ここが盲点        ↑対策①     ↑対策②

多くのチームではダウンロードとファインチューニングをシームレスにつなぎ、スキャンステップを持たないパイプラインを構築しがちです。上記の「スキャン → 形式変換」の2ステップを挟むだけで、AIサプライチェーン攻撃の大半を防御できます。

シナリオ:HuggingFace Hub利用時の注意点

HuggingFace Hub自体はpickleスキャンを実施していますが(公式セキュリティドキュメント参照)、完全ではありません

  • Hub側のスキャンは既知シグネチャのみ対応
  • ユーザー独自の難読化・暗号化手法には対応不可
  • モデルファイルの更新タイミングでスキャン結果が古くなる可能性

したがって、Hub側のスキャンを「信頼の根拠」にせず、ダウンロード後の自前スキャンを必須プロセスとして組み込む姿勢が重要です。

よくある質問(FAQ)

FAQ

HuggingFace pickleセキュリティ FAQ

MLエンジニアからよく寄せられる質問

pytorch_model.bin と model.safetensors の両方があるモデルはどちらを使うべきですか?

必ず model.safetensors を使ってください。HuggingFace Transformersライブラリはv4.28以降、safetensorsファイルが存在する場合は自動的にそちらを優先します。明示的に use_safetensors=True を指定することで、pickle形式へのフォールバックを無効化できます。

weights_only=True を設定すれば pickle は安全に使えますか?

完全には安全ではありません。weights_only=True はPythonの任意オブジェクト実行を制限しますが、テンソルデータとして許可されているデータ型の処理中に脆弱性が生じる可能性は残ります。safetensors形式への完全移行が最も確実な対策です。weights_only=True は移行期間中の暫定措置として位置づけてください。

HuggingFace Hub がスキャン済みなら独自スキャンは不要ではないですか?

Hub側のスキャンは既知シグネチャに基づく自動スキャンであり、未知の難読化手法や新種のインジェクションパターンには対応できません。独自スキャンとHubのスキャンは補完関係にあり、「Hubがスキャン済みだから安全」という判断は危険です。特に本番環境で使用するモデルは、自前スキャンを必須プロセスとして組み込んでください。

picklescanとmodelscanはどちらを使うべきですか?

新規導入であれば modelscan を推奨します。HuggingFaceが公式に推奨しており、対応フォーマットが広く(PyTorch、TensorFlow、Keras、NumPyなど)、SafeZipなど高度な攻撃パターンにも対応しています。picklescanは軽量で高速ですが、誤検知問題が報告されているため、本番環境での主要ツールとしてはmodelscanを選ぶのが安全です。

safetensors形式に変換する際のリスクはありますか?

変換作業自体にリスクがあります。既存のpickleファイルを torch.load() でロードして変換するため、クリーンな隔離環境(専用VM、Dockerコンテナなど)での実施を強く推奨します。変換後は元のpickleファイルを削除し、safetensors形式のみを保管してください。

CVE-2026-25874 は自分のプロジェクトに影響しますか?

LeRobotフレームワーク(Hugging FaceのロボットAIライブラリ)を使用している場合は影響を受ける可能性があります。LeRobotを使っていない場合でも、CVE-2026-25874はpickleデシリアライズの一般的なリスクを示す具体例として参考になります。LeRobotを使用している場合は、HuggingFaceの公式セキュリティアドバイザリで影響バージョンとパッチ情報を確認してください。

まとめ

モデルファイルのセキュリティは「ランタイム攻撃」ではなく「ダウンロード時点の攻撃面」です。CVE-2026-25874とJFrogの研究が示すように、HuggingFace Hub上の悪意あるモデルや、pickle形式の設計的欠陥は現実の脅威として存在しています。

今日から始められる3つの対策を再確認します:

  1. スキャン: modelscan を導入し、ダウンロードしたモデルファイルをロード前に必ずスキャンする
  2. safetensors移行: 新規プロジェクトはsafetensors形式のモデルのみを許可する。既存プロジェクトは段階的に移行する
  3. CI/CD統合: GitHub ActionsにscanステップをPRゲートとして組み込み、チーム全体で一貫した検査を維持する

PyTorch Foundationがsafetensorsを正式プロジェクトとして採用したことは、この問題がコミュニティ全体で認識されているサインです。「モデルファイルは信頼できる」という前提を捨て、ダウンロードしたモデルファイルは実行可能コードと同等のリスクを持つという認識でパイプラインを設計し直しましょう。

次に読むおすすめ

概要を掴んだ次のステップとして、実践編の詳細ガイドをnoteで公開しています。

noteで続きを読む

関連記事

参考リンク