こんにちは!セキュリティの世界へようこそ。
新人のIT担当者や、これからWebアプリケーションの安全性を守っていきたい開発者の方に向けて、少しドキッとするけれどとっても大切なセキュリティのお話をしていきたいと思います。
今回のテーマは「Insecure Deserialization(安全ではないデシリアライズ)」です。名前からしてなんだか呪文のようで難しそうですよね。でも大丈夫です!身近な「宅配便の荷物」と「合い鍵」の例えを使いながら、一歩ずつ優しく紐解いていきますので、一緒にリラックスして学んでいきましょう!
—
1. デシリアライズってなに?(宅配便の荷物に例えてみよう)
Webアプリケーションを作ったり使ったりしていると、プログラムの中で使っている複雑なデータ(ユーザーのプロフィールやカートの中身など)を、別の場所に送ったり保存したりしたい場面がたくさん出てきます。
ここで問題になるのが、「プログラムが記憶している複雑なデータ構造のままでは、ネットを通じてきれいに送れない」ということです。
そこで登場するのが、「シリアライズ(直列化)」という技術です。
これは、プログラムのデータを、まるで「パッキングして段ボール箱に詰め込んだ状態」にする作業だと思ってください。
- シリアライズ(荷造り):複雑なオブジェクトを、通信や保存しやすいように「ただの文字列(バイト列)」に変換すること。
- デシリアライズ(荷解き):送られてきた文字列を、再びプログラムが扱える「もとの複雑なオブジェクト」に戻すこと。
ネットの向こうから送られてきた段ボール箱(文字列)を受け取って、家のリビングでパカーッと荷解き(デシリアライズ)するのが、Webシステムの裏側で行われている日常なんです。
—
2. 恐ろしい「Insecure Deserialization」の仕組み
さて、ここからが少し危ないお話です。
もし、あなたがネット通販の荷物を受け取るとき、「誰が送ったか分からない、怪しい段ボール箱」を中身も確認せずにリビングの真ん中で盛大に荷解きしてしまったらどうなるでしょうか?
もしかしたら、その段ボール箱の中に入っているのは、注文したおもちゃではなく、「勝手に部屋の鍵を開けてしまう危険な工作機械」かもしれませんよね。
これが「Insecure Deserialization(安全ではないデシリアライズ)」の正体です。
攻撃者は、アプリケーションが信用しきっているデシリアライズ処理の隙を突き、悪意あるデータ(巧妙に作られたオブジェクト)を送り込みます。システム側がそれを「おっ、荷物が届いたな!」と何の疑いもなく荷解き(デシリアライズ)した瞬間、プログラムの内部で意図しない命令が実行されてしまい、最悪の場合はサーバーが乗っ取られるリモートコード実行(RCE)につながってしまうのです。
—
3. ガジェットチェーンってどういうこと?
攻撃者が送り込む荷物の中身には、ただの文字データだけでなく、プログラムを動かすための仕掛けが隠されています。その代表例が「ガジェットチェーン」と呼ばれるものです。
難しく聞こえますが、これも身近なもので例えてみましょう。
防犯の厳しいお家に泥棒が入ろうとするとき、いきなり玄関の分厚い鉄板を壊すのは大変ですよね。そこで泥棒は、家の中にある様々な「便利な道具(ガジェット)」を次々に組み合わせて利用します。
1. まず、窓の近くにあった「小さな磁石(ガジェット1)」を動かす。
2. その磁石のせいで「自動カーテンのスイッチ(ガジェット2)」が押される。
3. カーテンが引っ張られた拍子に「鍵を開けるレバー(ガジェット3)」が引っ張られてしまう。
このように、プログラムが本来持っている部品(クラスやメソッド)を数珠繋ぎ(チェーン)のように組み合わせて、攻撃者が望む悪さをさせる仕組みを「ガジェットチェーン」と呼びます。攻撃者はゼロから強力なウイルスを作るのではなく、「アプリにもともと備わっている便利な機能」を悪用して、意図しない連鎖反応を起こすわけですね。
—
4. コードで見る危険なデシリアライズの例(PHPの場合)
百聞は一見にしかず。PHPというプログラミング言語を例に、安全ではないデシリアライズがどのように実装されてしまっているかを見てみましょう。
以下のコードは、ユーザーからの入力(Cookieなど)をそのまま unserialize() 関数で荷解きしてしまっている非常に危険な例です。
<?php
// 【注意】これは脆弱なコードのサンプルです(本番環境では絶対に書かないでください!)
class UserProfile {
public $username;
public $isAdmin = false;
// オブジェクトが破棄されるときに自動で呼ばれるマジックメソッド
public function __destruct() {
if ($this->isAdmin) {
echo "管理者権限で特別な処理を実行します!";
}
}
}
// ユーザーから送られてきたCookieの値をそのままデシリアライズ(荷解き)している
// ※もし攻撃者が巧妙に作られた文字列を送ってきたら大変なことになります...
if (isset($_COOKIE['session_data'])) {
$userInput = base64_decode($_COOKIE['session_data']);
$userObject = unserialize($userInput); // ← ここが脆弱なポイント!
}
?>
このように、ユーザーから受け取ったデータを何の検証もせずに unserialize() やそれに類するデシリアライズ処理に渡してしまうと、攻撃者に裏口を開けてしまう結果になります。
—
5. 一歩ずつ学ぶ!安全なデシリアライズのための対策と防御
「じゃあ、デシリアライズを使うときはどうやって身を守ればいいの?」という疑問がわきますよね。
ここからは、実務で使える具体的な防御のアプローチを優しく見ていきましょう。
対策1:そもそも信頼できないデータをデシリアライズしない
これが一番確実な防犯対策です。ユーザー(外部)から受け取ったデータを、そのままオブジェクトに戻すような設計は極力避けましょう。
代わりに、安全なデータフォーマットである JSON(json_decode や json_parse)などを利用するのが現代のWeb開発の主流です。JSONはデータの構造を復元するだけで、勝手にプログラムの「処理(メソッド)」を暴走させるような仕組みを持たないため、安全性が高いのが特徴です。
対策2:型チェックやホワイトリストによる厳格な制御
もしどうしてもオブジェクトの復元が必要な場合は、「届いた荷物が本当に信頼できるものか」を厳しくチェックする仕組み(ホワイトリスト)を設けましょう。
<?php
// 安全なデシリアライズの考え方の例
$userInput = $_POST['payload'];
// 1. まずデータが安全な形式か、想定されたクラス名が含まれていないかチェックする
// 2. PHP 7.0以降の unserialize() では、allowed_classes オプションで許可するクラスを制限できます
$data = unserialize($userInput, ['allowed_classes' => ['App\Models\SafeUserDTO']]);
if ($data === false) {
// 荷解きに失敗した場合のエラーハンドリング
// 不正なデータが混入している可能性をログに記録する
error_log("不正なデシリアライズ試行を検知しました。");
}
?>
このように、allowed_classes などのパラメータを活用して、「あらかじめ許可された安全な設計図(クラス)の荷物しか荷解きしない」ように鍵をかけておくことが、現場でエンジニアが実践すべき大切な防御策になります。
—
まとめ
今回は、Insecure Deserialization(安全ではないデシリアライズ)の仕組みと、ガジェットチェーンの怖さ、そして具体的な対策についてお話ししました。
- シリアライズはオブジェクトをデータ(文字列)に荷造りすること。
- デシリアライズはその荷解きをすること。
- 信用できない荷物をそのまま荷解きすると、アプリ内の部品(ガジェット)を悪用されてしまう。
- 対策として、JSONを活用したり、
allowed_classesなどのホワイトリスト制御で許可されたものだけを受け入れるようにする。
セキュリティの対策は、一度にすべてを完璧にするのは難しいものです。ですが、「外部からの入力はうのみにせず、必ず疑ってかかる」「安全なフォーマットを選ぶ」という意識を一つずつ持っていくことで、あなたの作るWebアプリケーションはぐっと強固で安全なものになっていきます。
一歩ずつ、確実にセキュリティのスキルを磨いていきましょうね!
コメント