flawopen.com/安全でないデシリアライズ/Python pickle の安全性
Python の pickle.load() は信頼できないデータに安全ですか?
いいえ。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:
- 信頼できないデータのデシリアライズ。攻撃者が読み込みデータを提供・改変できるかで影響が決まります。
信頼できないバイト列が pickle に届く
アップロード、リクエスト、キャッシュ、成果物から攻撃者が操作した pickle がプロセスに入ります。
Unpickler がグローバルを解決する
復元命令を処理し、find_class() が関数を含む名前付きグローバルをインポートして返すことがあります。
読み込み中に動作が起きる
load() や loads() の実行中、結果をアプリが検証する前に関数が呼ばれる場合があります。
影響範囲はプロセス権限で決まる
動作は Python プロセスの権限で実行されます。ネットワーク、秘密情報、ファイル権限、隔離が影響を左右します。
pickle の読み込みがコード実行境界を越える理由
pickle ストリームはグローバルと復元関数を指定できます。Unpickling 中に Unpickler.find_class() が関数などのグローバルを解決し、opcode が復元動作を起こします。危険なのは pickle.load() または pickle.loads() 自体であり、後から execute を呼ぶ必要はありません。コードはプロセス権限で動きます。これは形式の仕様上の動作なので、信頼できないデータの交換に pickle を使わないでください。
JSON で解決できること、できないこと
Python の文書は、信頼できない、または改ざんされた pickle の読み込みが任意コードを実行し得ると警告しています。一方、JSON のデコード自体はそのコード実行経路を作りません。JSON も自動で入力検証はしません。サイズ、トップレベル型、フィールド、値をスキーマで検証し、認可も別に行います。旧システムで pickle が必要なら、保護された鍵で正確なバイト列を読み込み前に認証します。署名は鍵の前提下で送信元と完全性を示すだけで、信頼された署名者の悪意あるデータを安全にはしません。データ専用形式へ移行してください。
- ✓リクエスト、アップロード、共有キャッシュ、出所不明の成果物を扱う経路から
pickle.load()とpickle.loads()を削除します。 - ✓JSON などのデータ専用形式を使い、サイズ、スキーマ、許可フィールド、値域、エンドポイント認可を検証します。
- ✓信頼された内部処理で一時的に pickle を使う場合、読み込み前に正確なバイト列の HMAC または署名を検証し、鍵を保護してプロセス権限を制限します。
- ✓コードとデータフローで pickle の利用箇所を探し、各データを書き換えられる主体を確認して移行または隔離します。
よくある質問
find_class() を制限すればリスクを下げられますが、全プロトコルと到達可能な動作に対して安全と証明するのは困難です。ユーザー入力には許可リストよりデータ専用形式を選びます。
はい。JSON はデコード中の pickle 型コード実行を避けますが、型、フィールド、サイズ、値、認可の検証は必要です。解析と業務ルールを分けてください。