この記事のポイント

  • pickle利用時のセキュリティ脆弱性をレビュー観点で整理できる
  • 実際に発生するコード注入経路とリスクが理解できる
  • 安全な代替手法と設計方針を具体的に学べる
  • セキュアコーディングレビュー力を強化できる

そもそもPickleとは

Python標準ライブラリに含まれるpickleは、Pythonオブジェクトをバイト列に変換(シリアライズ)したり、その逆(デシリアライズ)を行う仕組みです。非常に柔軟でほぼ任意のPythonオブジェクトをそのまま保存できます。

簡単なpickle利用例
import pickle

data = {"key": "value", "number": 123}
serialized = pickle.dumps(data)
restored = pickle.loads(serialized)
  • dumps(): Pythonオブジェクト→バイナリ化
  • loads(): バイナリ→Pythonオブジェクト復元

この手軽さから、簡易キャッシュ、ファイル保存、一時的なセッション情報保存など幅広く利用されています。

実務利用例
  • 機械学習モデルの学習済み重み保存
  • Webアプリケーションの一時セッション保存
  • 分散ジョブキューのデータ渡し
  • 内部専用ツールの設定状態保存

なぜこれをレビューするのか

pickleは極めて強力な反面、設計次第で重大なセキュリティ脆弱性を引き起こします。レビューアーとしては以下を読み解く必要があります。

レビューアー視点

  • pickle.loads()の入力経路が外部依存していないか
  • 攻撃者が任意コードを混入できる状況になっていないか
  • 安全なシリアライズ形式が選定されているか
  • pickle利用箇所が限定管理されているか
  • 代替技術(json, protobuf, msgpack等)を検討しているか

開発者視点

  • pickleは楽で便利なので安易に使ってしまいがち
  • 内部専用用途なら安全と思い込みやすい
  • 後から外部I/Oに切り替わる設計変更で危険化することに無自覚
  • pickleの設計責務整理の重要性に気づきにくい

Pickleの危険性の構造

pickleの最大の問題は「任意のPythonコードを含められる」ことにあります。
pickleデータは単なる値の保存ではなく、復元時にクラス解決・関数呼び出しまで行います。
これが「コード実行可能なシリアライズフォーマット」という危険性を生みます。

攻撃例

攻撃用pickle生成例
import pickle
import os

class Exploit:
    def __reduce__(self):
        return (os.system, ("echo ATTACKED",))

payload = pickle.dumps(Exploit())
pickle.loads(payload)  # 実行時にecho ATTACKEDが実行される
  • __reduce__()メソッドはpickle復元時の実行内容を指定可能
  • 結果として、任意コマンド実行が復元時に発生する
攻撃経路は以下の通り
  • pickleファイルの改ざん
  • API受信パラメータへのpickle混入
  • セッションID等にpickle利用 → セッション固定攻撃
  • キュー/バッチ処理用pickleメッセージの外部汚染

PlantUML:攻撃経路の流れ

UML Diagram

良い実装例(pickle非使用設計)

そもそもpickle利用自体を避け、「コード実行不能なシリアライズ形式」を採用するのが安全設計の基本方針です。

安全なjsonシリアライズ例
import json
from datetime import datetime

class ApiRequestLog:
    def __init__(self, request_id, endpoint, response_code, client_ip, requested_at):
        self.request_id = request_id
        self.endpoint = endpoint
        self.response_code = response_code
        self.client_ip = client_ip
        self.requested_at = requested_at

    def to_dict(self):
        return {
            "requestId": self.request_id,
            "endpoint": self.endpoint,
            "responseCode": self.response_code,
            "clientIp": self.client_ip,
            "requestedAt": self.requested_at.isoformat(),
        }

# シリアライズ
data = ApiRequestLog(1, "/home", 200, "192.168.0.1", datetime.now())
serialized = json.dumps(data.to_dict())

# デシリアライズ
restored_dict = json.loads(serialized)
  • jsonは純粋なデータ構造のみ保存
  • 任意コード実行能力がそもそも存在しない

レビュー観点整理

pickle利用箇所をレビューする際は以下を観察します。

  • pickle.loads()呼び出しが外部入力から分離されているか
  • pickle使用範囲がファイルI/O専用・内部利用限定になっているか
  • 長期保存やネットワーク送受信でpickleを用いていないか
  • 代替フォーマット検討が実施されているか
  • 開発者がpickleの危険性を十分認識して設計しているか

良くない実装例: ケース1(外部入力pickle)

危険なpickle受信例
# dangerous_api.py

import pickle
from flask import request

@app.route("/restore", methods=["POST"])
def restore():
    payload = request.data
    obj = pickle.loads(payload)
    process(obj)
@Reviewer
外部受信データをそのままpickle.loads()しています。任意コード実行リスクが極めて高い設計です。使用を中止してください。

問題点解説

  • request.dataは攻撃者完全コントロール下
  • 攻撃pickleを送信するだけで任意コード実行が成立
  • 防御策が存在せずゼロデイ的に悪用可能

良くない実装例: ケース2(セッションpickle)

セッション用pickle利用例
# session_manager.py

import pickle

class SessionStore:
    def __init__(self, backend):
        self.backend = backend

    def save_session(self, session_id, data):
        self.backend.set(session_id, pickle.dumps(data))

    def load_session(self, session_id):
        payload = self.backend.get(session_id)
        return pickle.loads(payload)
@Reviewer
セッションデータにpickleを使用しています。攻撃者がセッションIDを固定・汚染すれば任意コード実行が成立します。pickle利用を避けてください。

問題点解説

  • セッションID奪取→pickle汚染が攻撃成立条件
  • pickleはシステム全体のセキュリティ境界を破壊する危険構造になる

改善例:安全な設計整理版

安全なシリアライズ設計
import json

class SessionStore:
    def __init__(self, backend):
        self.backend = backend

    def save_session(self, session_id, data: dict):
        self.backend.set(session_id, json.dumps(data))

    def load_session(self, session_id):
        payload = self.backend.get(session_id)
        return json.loads(payload)
データ設計の本質は「コード実行能力を持たせない」

pickleは設計上「データ+振る舞い=危険構造」になりがちです。
JSON・Protobuf・MessagePack等は「純粋データ構造」であり、安全性が高まります。

PlantUML:安全設計の整理ブロック図

UML Diagram

Pickle禁止設計は「文化」になる

レビューアーとして重要なのは個別修正提案ではなく「開発文化の指導」です。

  • pickle利用禁止のチーム規約を明文化
  • JSON・Protobuf・TypedDict等の型安全データ文化を推進
  • 新人教育でpickle危険性を必ず解説
  • 既存コードレビューでpickle駆除提案を継続

観点チェックリスト

まとめ

pickleは「便利すぎるが故に設計責任を放棄しやすい機構」です。
コードレビューでは「pickleを見つけた瞬間に立ち止まる」反射神経を養うことが安全設計の基本です。
安全で透明性の高い純粋データシリアライズ設計を推進し、pickle依存を排除する文化を確立することが長期的な品質保証に繋がります。