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

XML外部エンティティ(XXE)の深淵:パーサーの盲点をつく内部ファイル読み取りとSSRFのアーキテクチャ防衛

ペネトレーションテストの現場において、未だにモダンなWebアプリケーションの背後で眠る「レガシーの呪い」に遭遇することは少なくない。その代表格がXML外部エンティティ(XXE:XML External Entity)脆弱性だ。

JSON全盛の現代であっても、エンタープライズ領域のシステム連携、SAMLによるシングルサインオン、SOAPベースのレガシーAPI、あるいはSVGファイルを取り扱う画像処理パイプラインなど、XMLパーサーが稼働する局面は至る所にある。そして、開発者がセキュリティコンテキストを意識せぬまま標準設定のXMLパーサーをインスタンス化した瞬間、攻撃者にとっての「内部ネットワークへの黄金の鍵」が生成される。

本稿では、XXEが内包する根本的なプロトコル仕様の欠陥から、ローカルファイル読み取り、そしてクラウド環境のメタデータサービスを標的としたServer-Side Request Forgery(SSRF)に至るまでの攻撃連鎖を、低レイヤのパーサー挙動と共に見つめ直す。さらに、単なる「外部DTDを無効化せよ」という陳腐なアドバイスを超え、実務のアーキテクチャに組み込むべき多層防御の設計論を詳解する。

—

1. XXEの根本原因:XML仕様とパーサーの設計思想

XXEの本質は、XMLの仕様(Extensible Markup Language (XML) 1.0)そのものに組み込まれた「文書型定義(DTD:Document Type Definition)」の機能、特に外部エンティティ参照メカニズムにある。

XMLパーサーは、文書の妥当性検証や構造定義を行うためにDTDを解釈する。この際、URI(Uniform Resource Identifier)を用いて外部リソースを指定し、その内容をパース中の文書にインクルードする機能が標準で備わっている。問題は、多くのプログラミング言語における標準XMLパーサーが、初期設定(Default Configuration)のままでは、信頼できない入力値に対して外部リソースの解決を無条件に許可してしまう点にある。

攻撃者は、この仕様を逆手に取り、アプリケーションが処理するXMLペイロード内に悪意あるDTDを注入する。パーサーはそれを忠実に解釈し、OSのファイルシステムや内部ネットワークへアクセスを試みるのだ。

—

2. 攻撃シナリオ:内部ファイル読み取りからクラウドSSRFへの昇格

実際のペネトレーションテストやレッドチーム演習において、XXEは単なる「情報漏洩」にとどまらない。コンテキストに応じた致命的なインパクトをもたらす。

ローカルファイル読み取り(Arbitrary File Read)

典型的なシナリオは、アプリケーションが返すレスポンス(エラーメッセージや処理結果)の中に、読み取ったファイルのコンテンツを反射(Reflection)させる手法だ。

以下は、脆弱なPHPアプリケーション(libxmlを使用)に対して送信する不正なXMLペイロードの概念例である。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
  <!-- 外部エンティティ「xxe」にローカルのpasswdファイルを指定 -->
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
  <name>&xxe;</name>
</root>

もしアプリケーション側で libxml_disable_entity_loader(false); が維持されている場合、パーサーは /etc/passwd の内容を読み込み、&xxe; の部分を展開して処理を続行する。結果として、レスポンス内に機密ファイルの中身が露わになる。

バイナリデータや複数行にわたるファイル(XMLの構文規則に違反する文字を含むもの)を読み取る場合は、php://filter などのストッパーを利用してBase64エンコードを行うテクニックが実戦では多用される。

クラウド環境におけるSSRFとメタデータ搾取

コンテナ化されたモダンなインフラやパブリッククラウド(AWS, GCP, Azure)上で稼働するアプリケーションにおいて、XXEはSSRFの踏み台として極めて高い脅威度を持つ。

AWSのEC2インスタンス上では、インスタンスメタデータサービス(IMDSv1)へ http://169.254.169.254/latest/meta-data/iam/security-credentials/ へアクセスすることで、IAMロールの一時クレデンシャルを容易に取得できる。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
  <!-- AWSのメタデータサービスへHTTPリクエストを強制する -->
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root>
  <data>&xxe;</data>
</root>

この攻撃が成功した場合、攻撃者はクラウドインフラの内部権限を奪取し、さらなる水平展開(Lateral Movement)やデータexfiltration(持ち出し)を実行可能になる。

—

3. 脆弱性を生むコードと、安全な実装の比較

開発現場における根本的な対策は、使用するXMLパーサーにおいて外部DTDの処理とエンティティの解決を明示的に無効化(Disable)することに尽きる。

脆弱な実装例(PHP / libxml)

以下のコードは、セキュリティ設定を一切行わずにユーザー入力をパースしている典型的なアンチパターンである。

<?php
// 【危険】デフォルト設定のままパースを実行しているためXXEに脆弱
$xmlInput = file_get_contents('php://input');

// 外部エンティティのロードが無効化されていない
$dom = new DOMDocument();
$dom->loadXML($xmlInput, LIBXML_NOENT); // LIBXML_NOENTは実体参照の置換を有効化するためさらに危険

$data = $dom->getElementsByTagName('name')->item(0)->nodeName;
echo "Processed: " . $data;
?>

堅牢な実装例(PHP / libxml)

現代のPHP(Libxml 2.9.0以降)では、外部エンティティのロードが無効化されていることがデフォルトになりつつあるが、明示的に制御することが鉄則となる。

<?php
// 【安全】外部エンティティのロード機能を明示的に無効化する
libxml_disable_entity_loader(true);

$xmlInput = file_get_contents('php://input');

$dom = new DOMDocument();
// LIBXML_DTDLOAD や LIBXML_NOENT は絶対に指定しない
// 安全なフラグのみを指定してロードを行う
$res = $dom->loadXML($xmlInput, LIBXML_NONET);

if ($res === false) {
    http_response_code(400);
    echo "Invalid XML payload.";
    exit;
}

// 安全に要素を取得して処理を継続
$data = $dom->getElementsByTagName('name');
if ($data->length > 0) {
    echo "Processed safely: " . htmlspecialchars($data->item(0)->nodeValue, ENT_QUOTES, 'UTF-8');
}
?>

Java(DocumentBuilderFactory)やPython(defusedxml)などの他言語エコシステムにおいても、同様に http://apache.org/xml/features/disallow-doctype-decl などのプロパティを用いて、DTD宣言自体を完全に拒否するアーキテクチャを採用すべきである。

—

4. チーフホワイトハッカーの視点:防御層のアーキテクチャ設計

コードレベルの修正は必須であるが、エンタープライズレベルのセキュリティ担保を目指すテックリードやセキュリティアーキテクトは、単一のコード修正に依存しない多層防御(Defense in Depth)を構築しなければならない。

1. ネットワーク層での隔離(Egress Filtering)
アプリケーションサーバーから外部インターネット(特にプライベートIPレンジやリンクローカルアドレス 169.254.169.254)への不要なアウトbound通信をファイアウォールやセキュリティグループで厳格にブロックする。万が一XXEが存在しても、SSRFとしての外部通信を物理的に遮断できる。

2. WAF(Web Application Firewall)によるシグネチャ検知の限界と補完
<!ENTITY や SYSTEM といったキーワードをWAFでフィルタリングすることは可能だが、エンコーディングの差異(UTF-16, UTF-32等)やカスタムDTDを用いた難読化によって容易にバイパスされる。WAFはあくまで「最後の網」であり、根本的な対策はアプリケーション層のパーサー設定にあることを忘れてはならない。

3. 入力バリデーションとスキーマ検証(XSD Validation)
信頼できないXMLを受け取る際は、事前に厳格なXML Schema(XSD)を用いてバリデーションを行い、許可された要素と属性のみを受け入れるパーシングパイプラインを構築する。この際も、XSDの読み込み過程で外部エンティティが解決されないよう、パーサーの設定が適切に維持されていることが前提となる。

結びにかえて

XXEは、枯れた技術であるがゆえにレガシーシステムやマイクロサービスの隙間にひそかに生き残り、インフラ全体の崩壊を招く強力なトリガーとなり得る。

セキュリティアーキテクトとして求められるのは、単に「脆弱性を潰す」ことではなく、「信頼できない外部入力を扱うすべてのコンポーネントにおいて、デフォルトの挙動を疑い、ゼロトラストの思想に基づいたパーサー設計を強制する」ガバナンスの確立である。コードの隅々まで目を光らせ、攻撃者の思考の一歩先を行く防御壁を築き上げてほしい。

コメント

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