CyberFix Note
セキュアコーディング

安全でないデシリアライゼーションの対策。信頼できないデータの復元が任意コード実行につながる仕組み

対象の目安: WebアプリとAPIの開発者 / 実務

ソウ攻撃・脆弱性リサーチ担当
・ 約13分で読めます
安全でないデシリアライゼーションの対策。信頼できないデータの復元が任意コード実行につながる仕組み

シリアライゼーションは、メモリ上のオブジェクトを保存や送信のためにバイト列や文字列へ変換する処理です。デシリアライゼーション(逆シリアライズ)は、そのバイト列や文字列を元のオブジェクトへ復元する処理を指します。セッション情報の保存、プロセス間のデータ受け渡し、キャッシュやメッセージキューへの格納など、アプリケーションのさまざまな場面で使われます。

問題が生じるのは、この復元を信頼できない発信元から受け取ったデータに対して行う場合です。MITREのCWE-502(Deserialization of Untrusted Data)は、復元後のデータが妥当であることを十分に保証しないまま信頼できないデータを復元してしまう欠陥として定義しています。この記事は、Webアプリケーションとapiの開発者に向けて、なぜオブジェクトの復元が任意コード実行やサービス妨害につながるのかを機構レベルで整理し、OWASPが示す根本対策と言語別の勘所をまとめます。攻撃の再現手順やペイロードは扱わず、仕組みと対策に絞ります。

シリアライゼーションと逆シリアライズの基礎

オブジェクトは、メモリ上ではフィールドの値やクラスの型情報を含んだ構造として存在します。これをファイルに保存したりネットワークで送ったりするには、一続きのバイト列や文字列へ変換する必要があります。この変換がシリアライゼーションで、逆に受け取ったバイト列から元のオブジェクトを組み立て直すのが逆シリアライズです。

多くの言語は、この処理を手軽に扱うためのネイティブな仕組みを備えています。JavaのObjectOutputStreamとObjectInputStream、Pythonのpickle、PHPのserializeとunserialize、.NETのBinaryFormatterなどが代表例です。これらは型情報を含めてオブジェクトをそのまま復元できる反面、復元の過程で、形式や言語に応じてコンストラクタやreadObjectなどのフック、あるいはマジックメソッドが自動的に呼び出されることがあります。呼び出される処理は言語や形式によって異なり、たとえばJavaの標準シリアライズでは対象クラス自身のコンストラクタは通常実行されず、readObjectなどのフックや、非Serializableな親クラスのコンストラクタが関わります。こうした自動的な処理の呼び出しが、後述する任意コード実行の足がかりになります。JSONやXMLのような純粋なデータ形式は、あくまで値の並びを表現するだけで、任意のクラスをその型のまま復元する機能を標準では持たない点が、ネイティブ形式との大きな違いです。

なぜ任意コード実行に至るのか

信頼できないデータの復元が危険なのは、復元の過程でアプリケーションが意図しない処理が動き得るためです。CWE-502は、この欠陥がもたらす主な影響として、データ整合性の侵害、サービス妨害(DoS)、そしてガジェットチェーンを介した任意コード実行を挙げています。データ整合性の侵害には、復元されるオブジェクトの改ざんや、通常の入口を迂回した想定外メソッドの呼び出しが含まれます。

ここで中心になるのがガジェットチェーンの考え方です。攻撃者は新しいコードをサーバーへ送り込むのではありません。復元時に、アプリやライブラリ内に既に存在するクラスの処理(コンストラクタや特定メソッド)が連鎖的に呼び出されることを悪用します。攻撃者が用意した状態を持つオブジェクトを復元させることで、既にある部品(ガジェット)をつなぎ合わせ、最終的に危険な処理へ到達させます。手元にある道具だけを組み合わせて目的の動作を作り出すイメージで、送り込むのはコードではなくデータである点が、この欠陥の見つけにくさにつながります。具体的な連鎖の作り方やペイロードはここでは示しません。

MITREのCWE-502は、復元後のデータが妥当であることを十分に保証しないまま信頼できないデータを復元してしまう欠陥と定義しています。主な影響として、データ整合性の侵害(オブジェクトの改ざんや想定外メソッドの呼び出し)、サービス妨害(DoS)、ガジェットチェーンを介した任意コード実行を挙げています。

信頼できないデータの復元がそのまま危険な処理につながる構図は、外部入力の扱いを誤ったときのRCEと重なります。入力の検証とエスケープの基本は、次の記事で整理しています。

あわせて読みたい

入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする

OWASP Top 10での位置づけ

安全でないデシリアライゼーションは、OWASP Top 10 2017では「Insecure Deserialization」として独立したカテゴリでした。その後、2021ではA08「Software and Data Integrity Failures(ソフトウェアとデータの整合性の不具合)」という広いカテゴリへ統合されました。2025でも同じくA08として引き続き扱われています。カテゴリ名がデシリアライゼーション単体から整合性の不具合全般へ広がったのは、署名されていない更新の受け入れや検証されないデータの信頼といった、整合性を保証しない設計の一種としてこの欠陥を位置づけ直したためです。

OWASPが示す典型的な攻撃例では、あるアプリでJavaのシリアライズ済みオブジェクトの署名であるrO0(base64表現)が見つかり、攻撃者がツールでその中身を改ざんしてサーバー上で任意コード実行に至る、というシナリオが示されています。Cookieやトークンにシリアライズ済みオブジェクトをそのまま格納している場合、その中身が外部から差し替えられ得るという構図です。

OWASP Top 10では、2017で独立カテゴリだったInsecure Deserializationが2021でA08 Software and Data Integrity Failuresへ統合され、2025でもA08として扱われています。攻撃例として、Javaのシリアライズ済みオブジェクトの署名rO0が見つかり、その中身を改ざんして任意コード実行に至るシナリオが示されています。

OWASP Top 10全体の構成と各カテゴリの読み方は、次の記事でまとめています。

あわせて読みたい

OWASP Top 10とは。開発者が押さえるべきWebの代表的リスクと対策を一気に理解する

根本対策と言語別の勘所

OWASPのDeserialization Cheat Sheetは、リスクを大きく減らせる有力な対策として、言語独自のネイティブなシリアライズ形式を避け、JSONやXMLのような純粋なデータ形式を使うことを挙げています。純粋なデータ形式であれば、値の並びとして扱われるため、任意のクラスをその型のまま復元してコードを走らせる余地が生まれにくくなります。ただし純粋なデータ形式へ移すだけで安全になるわけではなく、多態的な型復元を許さない、危険なXMLパーサ設定を避ける、DTOや固定スキーマを使うといった前提とあわせて初めて効きます。あわせて、信頼できない発信元からシリアライズ済みオブジェクトを受け入れないことが土台になります。

どうしてもネイティブ形式が必要な場合は、HMACなどのメッセージ認証コードによる完全性検証、またはデジタル署名で改ざんを検知し、復元できるクラスを許可リスト(ホワイトリスト)で限定します。完全性を検証しても、正規の送信元自体が危険なオブジェクトを作れる場合は防げないため、復元できる型の許可リストと必ず併用します。拒否リスト(ブラックリスト)は新しいガジェットが次々と見つかるため単独では不十分で、許可リストを優先します。言語別の危険なapiと避け方は次のとおりです。

言語危険なAPI避け方
JavaObjectInputStreamresolveClassのオーバーライドかObjectInputFilterで復元できる型を制限(SerialKiller等のライブラリもある)
Pythonpickle、yaml.load、jsonpicklepickleとjsonpickleを避け、yamlはSafeLoaderを使う
PHPunserialize信頼できない入力にunserializeを使わない
.NETBinaryFormatterBinaryFormatterを使わず、Json.NETのTypeNameHandlingはNoneにする

危険なapiを避けたうえで、逆シリアライズを行う処理を低い権限で動かし、ログと監視を置き、ネットワーク的に隔離するなどの多層防御を重ねます。単一の対策に頼らず層を重ねることで、ある層をすり抜けられても被害を抑えられます。

OWASP Deserialization Cheat Sheetは、リスクを大きく減らせる有力な対策として言語独自のネイティブ形式を避けJSONやXMLを使うことを挙げます。ネイティブ形式が必要な場合はHMACなどのメッセージ認証コードやデジタル署名で改ざんを検知し、復元できるクラスを許可リストで限定します。JavaはObjectInputFilter、PythonはyamlのSafeLoader、PHPはunserializeの回避、.NETはBinaryFormatterの不使用などを言語別に示しています。

対策を導入の順序に落とすと、次のように進められます。

  1. 1

    信頼できない入力の復元経路を洗い出す

    外部から受け取ったデータをネイティブ形式で逆シリアライズしている箇所を、Cookieやトークン、キュー、キャッシュ、API受信を含めて洗い出します。どこで信頼できないデータの復元が起きているかを把握することが起点になります。

  2. 2

    純粋なデータ形式へ置き換える

    可能な箇所は、ネイティブ形式をやめてJSONやXMLのような純粋なデータ形式へ置き換えます。値の並びとしてデータを受け渡す設計にすることで、任意クラスの復元によるコード実行の余地を減らします。

  3. 3

    残るネイティブ形式に完全性チェックと許可リストを施す

    置き換えが難しく残ったネイティブ形式には、HMACなどのメッセージ認証コードやデジタル署名で改ざんを検知し、復元できるクラスを許可リストで限定します。完全性の検証だけでは正規の送信元が作った危険なオブジェクトは防げないため、必ず許可リストと併用します。JavaならObjectInputFilter、.NETならJson.NETのTypeNameHandlingをNoneにするといった型制限を組み合わせます。

  4. 4

    権限分離と監視で多層防御にする

    逆シリアライズを行う処理を低い権限で動かし、ログと監視を置き、ネットワーク的に隔離します。復元処理が想定外の挙動を示したときに検知と封じ込めができる状態にします。

信頼できないデータの逆シリアライズがRCEに転じた実例として、SharePointのCVE-2026-45659があります。問題の構造が重なる事例として、次の記事で扱っています。

あわせて読みたい

SharePoint Serverの認証済みRCE(CVE-2026-45659)。KEV収録を受けた対応の優先度

外部入力を起点にライブラリ内の処理が連鎖してRCEに至る点では、Log4Shellとも構図が共通します。教訓は次の記事で整理しています。

あわせて読みたい

Log4Shellに学ぶ、依存ライブラリ脆弱性の怖さとSBOM・SCAによる対策

まとめ

安全でないデシリアライゼーション(CWE-502)は、信頼できないデータの復元が任意コード実行やサービス妨害、整合性の侵害につながる欠陥です。攻撃者は新しいコードではなく、アプリやライブラリ内に既にある部品をガジェットチェーンとしてつなぎ、危険な処理へ到達させます。OWASP Top 10では2017の独立カテゴリから2021と2025のA08へ統合され、整合性を保証しない設計の一種として位置づけられています。対策の要点は、言語独自のネイティブ形式を避けてJSONやXMLのような純粋なデータ形式を使うことを軸に据え、それだけで安全になるわけではないため、多態的な型復元を許さないことや、残るネイティブ形式への完全性検証と許可リストによる型制限、権限分離と監視を重ねることにあります。信頼できない発信元からシリアライズ済みオブジェクトを受け入れない、という原則を設計の土台に置くことが出発点になります。

安全でないデシリアライゼーション対策で確認したいポイント

  • 外部から受け取ったデータをネイティブ形式で逆シリアライズしている箇所を、Cookieやトークン、キュー、キャッシュ、API受信を含めて洗い出したか
  • 可能な箇所は、ネイティブ形式をやめてJSONやXMLのような純粋なデータ形式へ置き換えたか
  • 残るネイティブ形式に、HMACなどのメッセージ認証コードやデジタル署名による完全性検証と、復元できるクラスの許可リストによる型制限を併用したか
  • 拒否リスト単独に頼らず、許可リストを優先しているか
  • JavaはObjectInputFilter、PythonはyamlのSafeLoader、PHPはunserializeの回避、.NETはBinaryFormatterの不使用とTypeNameHandlingのNone化を確認したか
  • 逆シリアライズを行う処理を低い権限で動かし、ログと監視、ネットワーク隔離による多層防御を重ねたか

出典・参考

この記事をシェア

関連する記事

セキュアコーディング

入力バリデーションと出力エスケープの原則。入口で検証し、出口で文脈別にエスケープする

セキュアコーディングの土台である入力バリデーションと出力エスケープを、それぞれの役割の違いから整理します。入口の検証は防御の一段目、出口の文脈別エスケープがXSSやインジェクションの根本対策である理由を、原理と実務の判断基準まで掘り下げます。