【テクニカル・上級編】 XML外部エンティティ(XXE)攻撃による機密ファイル読み取り – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

XML外部エンティティ(XXE)の深淵:パーサーの「善意」を悪用する攻撃アーキテクチャ

多くのエンジニアは、XMLを「枯れた技術」と見なし、レガシーなデータ交換フォーマットとして軽視している。しかし、その「枯れた」パーサーの内部には、設計上の原罪とも言える「外部リソースへの自動解決」という機能が眠っている。XXE(XML External Entity)攻撃は、単なる入力値の不備ではない。XMLパーサーという複雑なステートマシンが持つ、OSレベルのファイルシステムへのアクセス権限を悪用する、極めてプリミティブかつ致命的な権限昇格攻撃だ。

1. 攻撃者が狙う「パーサーの善意」という脆弱性

XMLの仕様(XML 1.0)には、DOCTYPE宣言内でENTITYを定義し、外部リソースを読み込む仕組みが存在する。これは本来、分散された文書の断片を結合するためのものだが、パーサーがセキュリティ制約なしにこれを処理すると、サーバー上の/etc/passwdやクラウド環境のメタデータAPI(http://169.254.169.254/)が丸裸になる。

攻撃者が狙うのは、パーサーが「外部リソースをフェッチする」という命令を実行する際の低レイヤ挙動だ。多くのパーサーは、URIスキーム(file://, http://, gopher://など)を解析し、バックエンドのネットワークスタックやファイルシステムAPIを呼び出す。この際、パーサー自体が実行ユーザー(多くの場合、Webサーバーの www-data や apache ユーザー)の権限を継承してしまう点が、インシデントの重大性を決定づける。

2. なぜ修正は後手に回るのか?

多くの開発者は、フレームワークが提供するデフォルト設定を盲信する。しかし、Javaの DocumentBuilderFactory やPHPの libxml は、歴史的な互換性を重視し、外部エンティティの解決をデフォルトで「有効」にしているケースが少なくない。

脆弱性を埋め込むのは、以下のような単純な処理だ。

// 脆弱な実装の例:外部エンティティを無効化していない
$doc = new DOMDocument();
$doc->loadXML($xml_input); // この時点で外部エンティティが解決されるリスクがある

攻撃者は、以下のようなペイロードを送り込むだけで、サーバーの心臓部を覗き見る。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

3. 鉄壁の防御:パーサー設定のハードニング

XXEを防ぐための唯一の絶対解は、パーサーの機能を「必要最小限に絞り込むこと」だ。現代的なアーキテクチャでは、XML自体を避けるのが最善だが、どうしても避けられない場合は、パーサーを完全に「ステートレスかつ孤立した状態」に設定しなければならない。

Java (DocumentBuilderFactory) での防御実装

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

// 外部エンティティの解決を完全に遮断する
String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
dbf.setFeature(FEATURE, true);

// 外部パラメータエンティティも無効化
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

// DTD自体を完全に無視する設定
dbf.setExpandEntityReferences(false);

PHP (libxml) での防御実装

// libxmlのバージョンに応じて外部エンティティ読み込みを明示的にオフにする
libxml_set_external_entity_loader(function ($public, $system, $context) {
    // 外部リソースへのアクセスを一切許可しない
    return null;
});

// パーサー実行時にフラグを付与
$doc = new DOMDocument();
$doc->loadXML($xml_input, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR); 
// ※注: LIBXML_DTDLOADを無効にするのが本質的

4. チーフホワイトハッカーの視点:次世代のセキュリティアーキテクチャ

XXEのような「入力を介したリソースアクセス」は、今後AIプロンプトインジェクションの防御にも通じる課題だ。LLMが外部ツールを呼び出す際、プロンプトに仕込まれた「偽の指示」が、パーサーが外部エンティティを解決する挙動と酷似している。

我々が今考えるべきは、以下のレイヤーでの防御だ。

  • サンドボックス化(隔離): XMLパース処理を、メインアプリケーションとは別の、ネットワークアクセスが一切遮断された特権の低いコンテナで実行する。
  • ガードレイルの実装: 入力値に対してスキーマバリデーション(XSD)を行い、DOCTYPE宣言が含まれる入力を事前に拒否するゲートウェイを配置する。
  • オブザーバビリティの強化: サーバーから外部への予期せぬアウトバウンド通信(DNSクエリやHTTPリクエスト)を、eBPFなどを用いてカーネルレベルで監視し、異常検知時にプロセスを即座にキルする。

XXEは、単なる設定ミスではない。これは、データが「コード(命令)」として解釈される可能性を持つ場所すべてに潜む、サイバーセキュリティの永遠のテーマである。パーサーの善意を信じるのではなく、その能力を徹底的に削ぎ落とすことこそが、攻撃者からシステムを守るための唯一の道だ。

コメント

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