XML External Entity(XXE)脆弱性まとめ
このたび、GMO Flatt Security 開発者応援プログラム for バイブコーダー に選定いただき、セキュリティ学習サービス 「KENRO」 を1か月無料で利用できることになりました。
「KENRO」は、実践的な脆弱性診断やセキュリティ演習を通じて、開発者が 攻撃者の視点 から安全なシステム設計を学べるオンライン学習プラットフォームです。
本記事では、KENROの学習内容をもとに XML External Entity(XXE)脆弱性 についてまとめます。
XML の基礎
<?xml version="1.0"?>
<response>
<message id="1">
<content>Hello!</content>
<by>Kenta</by>
</message>
</response>
- XML はタグの入れ子(木構造)で情報を表現する
- DTD(Document Type Definition)やスキーマで文書構造を定義できる
- 外部データ参照の取り扱いが脆弱性に直結することがある
DTD とエンティティ(Entity)
- DTD 内で
<!ELEMENT>や<!ATTLIST>により構造を定義できる - 名前付き定数(エンティティ)を宣言し、
&name;で参照可能 -
SYSTEM "file:///..."やSYSTEM "http://..."と指定すると External Entity(外部エンティティ)となり、外部リソースを参照できる
<!DOCTYPE top [
<!ENTITY secret SYSTEM "file:///etc/passwd">
]>
<top>&secret;</top>
このような宣言を解決する設定のまま XML を処理すると、サーバ側のファイルが読み出される危険がある。
XXE(XML External Entity)とは
XXE は、XML パーサが DTD/外部エンティティを制限なく解釈する環境で発生する脆弱性。
攻撃者は XXE を利用して、以下のような攻撃を行うことができる。
- 機密ファイルの読み出し
- DoS(処理資源枯渇)
- SSRF(サーバ経由での内部ネットワーク探索)
仕様上は単純だが、運用・設定の差異により発生しやすく、影響範囲が大きい。
攻撃例
1. サーバ内ファイルの読み出し
攻撃者が以下のような XML を送信すると、パーサが外部エンティティを解決し、/etc/passwd の内容が展開される可能性がある。
<!DOCTYPE top [
<!ENTITY secret SYSTEM "file:///etc/passwd">
]>
<top><name>&secret;</name></top>
2. Denial of Service(Billion Laughs / XML Bomb)
再帰的なエンティティ展開により、展開量が指数的に増加し、CPUやメモリを大量消費させる。
<!DOCTYPE root [
<!ENTITY e0 "ha">
<!ENTITY e1 "&e0;&e0;&e0;">
<!ENTITY e2 "&e1;&e1;&e1;">
<!ENTITY e3 "&e2;&e2;&e2;">
]>
<root>&e3;</root>
このような攻撃は「Billion Laughs」や「XML Bomb」と呼ばれる。
3. SSRF(Server-Side Request Forgery)
外部エンティティに http://internal/secret などを指定し、サーバ自身に内部リソースへアクセスさせる。
クラウド環境では特に http://169.254.~.~(メタデータサービス)が狙われやすく危険。
実践:脆弱なサンプルと挙動(概観)
以下は、C# における脆弱な設定例。
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlUrlResolver(); // ← 外部参照を許可
settings.DtdProcessing = DtdProcessing.Parse; // ← DTD を許可
using (XmlReader reader = XmlReader.Create(stream, settings))
{
XDocument doc = XDocument.Load(reader);
var name = doc.Descendants("name").First().Value;
}
上記のように XmlResolver を有効にし、DtdProcessing.Parse のままにすると、攻撃者が送った外部エンティティを解決してしまい、情報漏洩や SSRF、DoS を引き起こす可能性がある。
対策(共通方針と言語別実装例)
共通方針(優先順)
- 可能なら XML を使わない(JSON 等へ置換)
- XML パーサで DTD/外部エンティティを明示的に無効化する
- 安全化済みライブラリ(例:
defusedxml等)を利用する - アップロード XML をそのまま出力しない/最小権限で解析する
- ライブラリとランタイムを常に最新化する
PHP
// 古いバージョン向けの対策例(環境に依存するため要確認)
libxml_disable_entity_loader(true);
libxml_set_external_entity_loader(function() { return null; });
※PHP のバージョン差に注意。明示的な無効化を行う。
Python
defusedxml の利用を強く推奨。
標準 SAX を使う場合の例:
import xml.sax
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_pes, False)
parser.setFeature(xml.sax.handler.feature_external_ges, False)
Java
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Go
標準 encoding/xml は外部エンティティを解決しないため原則安全。
ただし libxml2 等を利用するライブラリを使う場合は DTD 無効化を確認する。
Ruby
Nokogiri 等はオプション次第で外部エンティティを展開する。
NOENT 等の展開オプションを付けないよう注意。
C# (.NET)
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit; // または Ignore
settings.XmlResolver = null; // 外部参照を無効化
using (XmlReader reader = XmlReader.Create(stream, settings))
{
XDocument doc = XDocument.Load(reader);
}
まとめ
こんな感じでXMLについてをまとめてみました。
あまり聞きなじみのない脆弱性だったので学びになりました