FlawOpen

flawopen.com/安全でないデシリアライズ/Python pickle の安全性

Python の pickle.load() は信頼できないデータに安全ですか?

CWE-502:信頼できないデータのデシリアライズリファレンスページ
要点

いいえ。pickle は受動的な記録ではなく、荷物に入ったプログラムのようなものです。読み込み時に、アプリがオブジェクトを検証する前に復元処理が動くことがあります。信頼境界を越える入力にはデータ専用形式を使いましょう。

危険 — 信頼できないバイト列は読み込み時にコードを起動し得る
import pickle

# Never accept pickle bytes from a request, upload,
# shared cache, or other untrusted source.
obj = pickle.loads(untrusted_bytes)
より安全な既定策 — データを解析してスキーマを検証する
import json

def parse_profile(raw: bytes) -> dict[str, str]:
    if len(raw) > 65_536:
        raise ValueError("Payload too large")

    try:
        value = json.loads(raw)
    except (json.JSONDecodeError, UnicodeDecodeError):
        raise ValueError("Invalid JSON") from None

    if not isinstance(value, dict) or set(value) - {"display_name", "theme"}:
        raise ValueError("Invalid profile")

    name = value.get("display_name")
    theme = value.get("theme")
    if not isinstance(name, str) or len(name) > 80:
        raise ValueError("Invalid display name")
    if theme not in {"light", "dark"}:
        raise ValueError("Invalid theme")

    return {"display_name": name, "theme": theme}
Pickle ストリーム:
オブジェクト復元を記述する Python 固有のバイト列。グローバル参照や復元動作を含みます。
Unpickling:
ストリームを解釈してオブジェクトグラフを復元する処理。関数を呼び出す場合があります。
信頼境界:
ユーザー、サービス、アップロード、共有ストレージがバイト列を操作できる箇所です。
CWE-502:
信頼できないデータのデシリアライズ。攻撃者が読み込みデータを提供・改変できるかで影響が決まります。
手順 1

信頼できないバイト列が pickle に届く

アップロード、リクエスト、キャッシュ、成果物から攻撃者が操作した pickle がプロセスに入ります。

手順 2

Unpickler がグローバルを解決する

復元命令を処理し、find_class() が関数を含む名前付きグローバルをインポートして返すことがあります。

手順 3

読み込み中に動作が起きる

load() や loads() の実行中、結果をアプリが検証する前に関数が呼ばれる場合があります。

手順 4

影響範囲はプロセス権限で決まる

動作は Python プロセスの権限で実行されます。ネットワーク、秘密情報、ファイル権限、隔離が影響を左右します。

pickle の読み込みがコード実行境界を越える理由

pickle ストリームはグローバルと復元関数を指定できます。Unpickling 中に Unpickler.find_class() が関数などのグローバルを解決し、opcode が復元動作を起こします。危険なのは pickle.load() または pickle.loads() 自体であり、後から execute を呼ぶ必要はありません。コードはプロセス権限で動きます。これは形式の仕様上の動作なので、信頼できないデータの交換に pickle を使わないでください。

JSON で解決できること、できないこと

Python の文書は、信頼できない、または改ざんされた pickle の読み込みが任意コードを実行し得ると警告しています。一方、JSON のデコード自体はそのコード実行経路を作りません。JSON も自動で入力検証はしません。サイズ、トップレベル型、フィールド、値をスキーマで検証し、認可も別に行います。旧システムで pickle が必要なら、保護された鍵で正確なバイト列を読み込み前に認証します。署名は鍵の前提下で送信元と完全性を示すだけで、信頼された署名者の悪意あるデータを安全にはしません。データ専用形式へ移行してください。

よくある質問

制限付き Unpickler なら攻撃者の pickle を安全にできますか?

find_class() を制限すればリスクを下げられますが、全プロトコルと到達可能な動作に対して安全と証明するのは困難です。ユーザー入力には許可リストよりデータ専用形式を選びます。

JSON にも検証が必要ですか?

はい。JSON はデコード中の pickle 型コード実行を避けますが、型、フィールド、サイズ、値、認可の検証は必要です。解析と業務ルールを分けてください。

一次資料

関連項目 安全でないデシリアライズ:リスクと対策 | flawopen.com Ruby YAML.load は安全?Psych のバージョンと safe_load .NET BinaryFormatter:リスクと移行 Java ObjectInputStreamの安全性とデシリアライズ対策 Newtonsoft.Json TypeNameHandling:安全な設定方法 PHP unserialize() と信頼できない入力