【実務・中級編】デシリアライゼーション脆弱性の仕組みと安全なデータ交換 – アプリケーションセキュリティ & 安全な開発防御ガイド

魔法の箱「シリアライズ」が招く地獄:デシリアライゼーション脆弱性を根絶する

現場でコードをレビューしていると、いまだに「オブジェクトをそのまま保存・転送したい」という強い誘惑に駆られた設計に出くわす。`serialize()`や`pickle.loads()`、あるいはJSONに隠された魔の手。これらは、攻撃者にとっての「リモートコード実行(RCE)の特急券」になり得る。

今日は、教科書的な「インジェクションに気をつけろ」といった抽象論は抜きにして、なぜこの脆弱性がシステムを崩壊させるのか、そして現場でどう防ぐのかを叩き込む。

1. なぜ「復元」が「実行」になるのか

シリアライズとは、メモリ上のオブジェクトをバイト列(または文字列)に変換し、保存や転送を可能にするプロセスだ。しかし、多くの言語のデシリアライザーは、「復元したデータが何者であるか」をチェックしない。

攻撃者は、クラスのプロパティを改ざんし、デシリアライズ時の「マジックメソッド(`__wakeup`や`__destruct`など)」を悪用する。これが引き金となり、本来呼ばれるはずのないシステムコマンドや、メモリ上の意図しない関数が実行される。

攻撃者の視点:PoCの概念

攻撃者は、アプリケーションが持つ既存のクラス(ガジェット)をパズルのように組み合わせ、RCEへ繋げる「ガジェットチェーン」を構築する。
例えば、PHPであれば、ログファイルを消去するクラスのプロパティを書き換え、`__destruct`が走った瞬間にWebシェルのパスを消し去るような挙動を強いる。これはWebアプリ側の権限で動くため、WAFのシグネチャをすり抜けることも珍しくない。

2. 実践的な防御:JSONと署名の鉄則

最も安全なルールは「外部から受け取ったバイナリをそのままデシリアライズしない」ことだ。データ交換には、言語特有のシリアライズ形式(PHPの`serialize`やPythonの`pickle`)は絶対に使ってはいけない。

JSON形式への移行と署名検証

構造化されたデータ交換にはJSONを使い、そのデータが「信頼できる送信元から来たものか」を署名(HMAC)で保証する。

Pythonでのセキュアな実装例

`pickle`を使わず、JSONと`hmac`による署名検証を組み合わせた実装例だ。

import hmac
import hashlib
import json
import base64

サーバー側で管理する秘密鍵(環境変数で管理すること!)
SECRET_KEY = b’super-secret-key-that-is-not-in-git’

def verify_and_load(data_str, signature):
“””署名を検証してからJSONをロードする”””
# HMAC-SHA256で改ざん検知
expected_sig = hmac.new(SECRET_KEY, data_str.encode(), hashlib.sha256).hexdigest()

if not hmac.compare_digest(expected_sig, signature):
raise ValueError(“改ざんされたデータです!”)

return json.loads(data_str)

使用例:安全なデータ処理
攻撃者が署名を偽造できない限り、中身の改ざんは不可能

3. インフラ・設定レベルでの防御

コードの修正には時間がかかる。緊急対応として、インフラ層で脆弱なエンドポイントを塞ぐ必要がある。

Nginx/WAFでの制限

特定のシリアライズパターンが含まれるリクエストをブロックする。例えば、`O:4:”User”`といったPHPのシリアライズ特有の文字列がパラメータに含まれていないか監視する。

ModSecurity (WAF) の設定例:

PHPのシリアライズ形式の攻撃パターンを拒否するルール
SecRule ARGS “@rx O:[0-9]+:\”[^\”]+\”:” \
“id:10001,phase:2,deny,status:403,msg:’Potential PHP Deserialization Attack'”

IAM・権限最小化の原則

万が一、デシリアライゼーションでコードが実行されてしまったとしても、被害を最小限に抑えるのがプロの仕事だ。

  • ファイルシステム: アプリケーションユーザーに書き込み権限を与えるディレクトリは、必要最低限に絞る(`/tmp`への無防備なアクセスは論外)。
  • アウトバウンド通信: サーバーが外部の不正なサーバーへ接続(リバースシェル)できないよう、Egressフィルタリングを厳格に設定する。

最後に:エンジニアへ伝えたいこと

デシリアライゼーションの脆弱性は、一度侵入を許せば「サーバーの乗っ取り」に直結する。だからこそ、開発初期段階で「シリアライズの禁止」をコーディング規約に盛り込むことが重要だ。

「便利だから」という理由で標準ライブラリのシリアライズ機能を使うのは、鍵のかかっていない玄関に貴重品を置くのと同じこと。この記事を読んだ今日からは、データの受け渡しには必ず「JSON + 署名」を標準装備としてほしい。

もし、今運用中のシステムで不安な箇所があるなら、まずはシリアライズされたデータを送信している全エンドポイントを洗い出すことから始めてみよう。それが、インシデントゼロを実現するための最初の一歩だ。

コメント

タイトルとURLをコピーしました