●
CWE-502 / Unsafe Deserialization
安全でないデシリアライズ:リスクと対策
信頼できない pickle、Java Serialization、PHP unserialize のデータが危険な動作を起こす仕組みと、置き換え・制限方法を解説します。
💡 やさしい説明 (ELI5)
シリアライズは模型の組み立て説明書を郵送するようなものです。デシリアライズは説明に従ってデータを再構成します。形式によっては再構成中にクラスを指定したり hook を呼んだりします。信頼できない説明を受け入れると、想定外の動作を起こす可能性があります。影響は形式、runtime、利用可能なクラス、アプリの状況によって異なります。
Core Concepts & Key Terms
- シリアライズ / デシリアライズ
- データやオブジェクトを保存・転送用の表現に変換し、プログラム内で解析または再構成すること。
- デシリアライズ hook
- Python __reduce__、Java readObject、PHP __wakeup / __unserialize など、オブジェクト再構成時に動く可能性がある callback。
- Gadget chain
- 既存ライブラリの動作をつなぎ、想定外の効果を起こす連鎖。利用可能性はアプリとライブラリのバージョンに依存します。
- デシリアライズフィルター
- オブジェクトストリーム読込時にクラスやグラフ指標を制限する方針です。リスクを下げますが、信頼できない native object stream の利用を推奨するものではありません。
How Unsafe Deserialization Can Become an Exploit Chain
Step 1
信頼できないバイト列がデコーダーに届く
Cookie、API body、queue message、保存レコードなどに攻撃者が影響できるデータが含まれます。
Step 2
アプリが native object format を呼び出す
コードが Python pickle、Java ObjectInputStream、PHP unserialize() などにバイト列を渡します。
Step 3
再構成中に形式固有の動作が起きる
形式や到達可能なクラスに応じて、constructor、hook、autoload、ライブラリメソッドがアプリの検証より前に実行されることがあります。
Step 4
影響は到達可能な動作次第
利用可能な gadget や hook が不正な影響を起こすことがありますが、入力が単に失敗したりリソースを消費したりする場合もあります。常にコード実行になるわけではありません。
Python / Flask: Unsafe Object Loading vs. Primitive JSON Validation
脆弱なパターン
# VULNERABLE: pickle can execute behavior during unpickling.
from flask import Flask, request
import pickle
app = Flask(__name__)
@app.post("/api/session")
def load_session():
# Never deserialize attacker-controlled bytes with pickle.
session = pickle.loads(request.get_data())
return {"user_id": session.user_id}
安全性を高めた検証
# SAFER: parse JSON primitives, then validate expected shape.
from flask import Flask, abort, request
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024
@app.post("/api/session")
def load_session():
payload = request.get_json()
if not isinstance(payload, dict):
abort(400, "Expected a JSON object")
user_id = payload.get("user_id")
if type(user_id) is not int or user_id <= 0:
abort(400, "Invalid user_id")
# Resolve authorization and session state server-side.
return {"user_id": user_id}
エンジニアリング向け強化チェックリスト
JSON にも入力サイズの上限と構造検証が必要です。従来型のオブジェクト形式では、クラスの許可リストだけに頼らず、デシリアライズ前にシリアライズ済みバイト列そのものを認証し、鍵を保護してください。MAC はデータを暗号化せず、リプレイも防ぎません。
- 信頼できない native object stream を Python pickle や PHP unserialize() で読み込まず、schema とサイズを検証した JSON などの基本データ形式を使います。
- Java Serialization をすぐに廃止できない場合は ObjectInputFilter の allowlist と object graph の上限を設定します。filter は常に既定で有効とは限りません。
- 既存形式の整合性を守る必要がある場合、管理された MAC 鍵でデシリアライズ前に正確なバイト列を認証します。MAC は暗号化でも replay 防止でもありません。
- セッション権限はサーバー側に保持するか、期限・audience・鍵ローテーションを備えた保守済み framework の署名トークンを使います。