このたび、GMO Flatt Security 開発者応援プログラム for バイブコーダー に選定いただき、 セキュリティ学習サービス 「KENRO」 を1か月無料で利用できることになりました!
「KENRO」は、実践的な脆弱性診断やセキュリティ演習を通じて、 開発者が 攻撃者の視点 から安全なシステム設計を学べるオンライン学習プラットフォームです。
本記事では、KENROの学習内容をもとに Insecure Deserialization 脆弱性 についてまとめます。
シリアライズ・デシリアライズとは
オブジェクト(クラスのインスタンス等)を何らかの形(例:バイト列や文字列)に符号化する操作を シリアライズ(serialize) と呼びます。
逆に、シリアライズされたデータから元のオブジェクトを復元する操作を デシリアライズ(deserialize) と呼びます。
多くのプログラミング言語はオブジェクトのシリアライズ・デシリアライズ用の関数を提供しています。以下は文字列配列を JSON 等でシリアライズ/デシリアライズする例です(言語や実装により差があります)。
// C# (例)
using Newtonsoft.Json;
string[] arr = new string[] { "a", "b", "c" };
string data = JsonConvert.SerializeObject(arr);
string[] data2 = JsonConvert.DeserializeObject<string[]>(data, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
シリアライズ/デシリアライズは非常に便利で、状態保存や復元を数行で実現できるため多用されます。しかし「ユーザ入力や外部データをそのままデシリアライズする」ことは危険を伴います。
Insecure Deserialization 脆弱性とは
ユーザなどの外部から与えられたデータを検証せずにデシリアライズすると、開発者が想定していないオブジェクトや値を生成させることができます。これが Insecure Deserialization(安全でないデシリアライズ) の本質です。
以下は PHP の簡単な例です。
<?php
// クラス Sample の定義
class Sample {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function hello() {
echo "Hello, $this->name!<br>";
}
}
// Cookie にシリアライズされたオブジェクトが記録されていたら復元、なければ新規生成
$instance = isset($_COOKIE['instance']) ? unserialize($_COOKIE['instance']) : new Sample('John');
// Cookie に $instance をシリアライズしたものを保存
setcookie('instance', serialize($instance));
// メソッド呼び出し
$instance->hello();
?>
このアプリを一度開くと Cookie にシリアライズ文字列が設定され、ユーザはそのCookieの値を自由に書き換えられます。結果として、本来用意していない入力(例:name を Mike に変更)を与えることが可能です。
デシリアライズが惹起するリスク
デシリアライズにより生成できるオブジェクトは、一般に以下の情報を含みます。
オブジェクトの型
オブジェクトの**メンバ(フィールド)**の値
(動的型付き言語では)メンバの型情報
実行可能なコード自体(関数のソースやバイトコード)は通常含まれないため、直接「コードを注入する」わけではありません。しかし、生成されたオブジェクトを通じてアプリケーションの振る舞いを変えられるため、次のような深刻な影響が発生します。
アプリケーションに予期せぬ入力が与えられる(例:値検証を回避)
結果として**任意のコード実行(RCE)**に繋がる可能性がある
<?php
class Sample2 {
private $hoge;
private $foobar;
public function __construct($hoge){
$this->hoge = $hoge;
$this->foobar = "Hello, ";
}
public function hello(){
// $this->foobar は定数なので安全に見える
echo $this->foobar . htmlspecialchars($this->hoge, ENT_QUOTES, 'UTF-8') . '!\n';
}
}
$name = isset($_GET['input']) ? $_GET['input'] : 'NONAME';
$instance = isset($_COOKIE['s2']) ? unserialize($_COOKIE['s2']) : new Sample2($name);
setcookie('s2', serialize($instance));
$instance->hello();
?>
上記では foobar はコンストラクタで固定されているため安全に見えますが、デシリアライズにより foobar を外部から書き換えられると、本来存在しなかった XSS が発生し得ます。
任意コード実行(RCE)を許すパターンと対策
1️⃣ 任意コード実行を許すパターン
(1) アプリケーションロジック経由の RCE
オブジェクト内のフィールドが system() や eval() 等の危険な関数に渡される場合、 攻撃者はそのフィールドをデシリアライズで操作することで 任意コマンドを実行 できます。
(2) マジックメソッド(特殊メソッド)経由の RCE
PHP の __destruct, __wakeup, __toString などのマジックメソッドは 暗黙に実行される ため、これらの中で危険な関数が呼ばれていると、デシリアライズで作られたオブジェクトが破棄されるタイミングで 任意コマンドが実行される可能性があります。
2️⃣ リスク評価のまとめ
Web アプリケーションが依存するライブラリやフレームワーク内に
危険なオブジェクト型(マジックメソッドを持つもの等) が多数存在する場合、
Insecure Deserialization は 任意コード実行に繋がり得る高リスクな脆弱性 です。
実際に「任意コード実行に繋がるか」の判定は難しく、
検査には多くの調査(使用ライブラリの把握、オブジェクト型の監査など)が必要です。
✅ 原則として、Insecure Deserialization は
「任意コード実行の可能性がある危険な脆弱性」として扱うべきです。
3️⃣ 対策(推奨方針)
🔹 可能ならオブジェクトの直接シリアライズ/デシリアライズを避ける
- JSON のような単純なフォーマットを用いる。
→ 表現力を制限することで危険度を下げられる。 - JSON を使う場合は 明示的なマッピングやバリデーションコード を用意する。
🔹 データの認証と整合性検証(デジタル署名)
- デシリアライズ対象データに署名を付与し、署名検証に失敗したデータは受け入れない。
- HMAC や公開鍵署名など、改ざん防止の仕組み を導入する。
🔹 ホワイトリスト方式のクラス許可
- 許可したクラス型のみ をデシリアライズ可能にする(型制限)。
- 一部ライブラリでは 型検査やセーフモード のオプションを提供している。
🔹 危険なメソッドの除外・安全化
-
__destruct,__wakeup等で外部入力を利用して危険な処理が呼ばれないよう設計する。 -
system,exec,eval等の 危険関数の使用を避ける。
🔹 最小権限の原則
- アプリケーション実行ユーザの権限を制限し、万が一 RCE が発生しても被害を抑える。
- コンテナや OS レベルでの 権限分離 を行う。
🔹 入力検証・バリデーション
- デシリアライズ前にデータ形式やサイズ等の検査を行う。
※ただし、デシリアライズ前の検査だけで完全に安全とは限らない。
🔹 監査・検査
- 使用ライブラリ・フレームワークのソースやドキュメントを確認し、
危険なオブジェクト型が含まれていないか監査する。 - 静的解析やペネトレーションテスト でデシリアライズ箇所を重点的に検査する。
まとめ
Insecure Deserialization 脆弱性について、攻撃者の視点から具体的な手法と対策をかなり深いところまで学べました。 KENROでは、実際にCTF形式で脆弱性を試したり、コードの改修を通して学べるため、とても完成度の高い学習サイトでした!