XML外部エンティティ(XXE)の脆弱性

XML外部エンティティ参照とパーサーの保護設定

説明

信頼できないXMLの解析でDTDや外部エンティティ参照を許可すると、ローカルファイルの読み取り、内部・外部アドレスへのリクエスト、エンティティ展開によるリソース消費を引き起こされるおそれがあります。

JAXPの XMLConstants.FEATURE_SECURE_PROCESSING を明示的に false にすると、実装のセキュリティ制限が無効になります。JDKのAPIで true を設定すると処理量と外部アクセスが制限されますが、実装やランタイムが異なる環境もあるため、この機能だけで完全なXXE対策とは考えないでください。DTDの使用と外部DTD・スキーマへのアクセスも個別に制限してください。

XMLReaderFactory はJava 9から非推奨のため、新しいコードには SAXParserFactory を使ってください。ただし、古いAPIの使用自体が脆弱性を意味するわけではありません。実際の保護設定と信頼境界を確認してください。

想定される影響

  • 機密ファイルの漏えい: アプリケーションが読み取れるローカルファイルが露出する可能性があります。
  • サーバーサイドリクエストフォージェリ(SSRF): 内部サービスや攻撃者のサーバーへリクエストを送られるおそれがあります。
  • サービス拒否: エンティティ展開やXML処理によって、CPUやメモリを使い果たす可能性があります。

対処方法

  1. 不要なDTDを拒否してください。 JDK 25ではJAXP設定またはJVMシステムプロパティの jdk.xml.dtd.support=deny を優先してください。パーサー単位では http://apache.org/xml/features/disallow-doctype-decl を true に設定します。
  2. 安全な処理と外部アクセス制限を明示してください。 XMLConstants.FEATURE_SECURE_PROCESSING を true にし、XMLConstants.ACCESS_EXTERNAL_DTD と XMLConstants.ACCESS_EXTERNAL_SCHEMA を空文字列にして外部プロトコルを拒否してください。
  3. DTDが必要なら許可範囲を絞ってください。 外部一般エンティティ、外部パラメーターエンティティ、外部DTDの読み込みを無効にしてください。必要なリソースだけをローカルXMLカタログや確認済みリゾルバーで解決し、有限の処理制限を維持してください。

例

変更前

java
import java.io.InputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;

public final class UnsafeXXE {
    static void parse(InputStream xml) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);

        SAXParser parser = factory.newSAXParser();
        parser.parse(xml, new DefaultHandler());
    }
}

安全な処理の制限を明示的に無効にし、DTDや外部アクセスも制限せずに入力を解析しています。

ローカルな保護設定を省略したインライン解析も確認してください。

java
import jakarta.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilderFactory;

public final class InlineServletXXE {
    static void parse(HttpServletRequest request) throws Exception {
        DocumentBuilderFactory.newInstance()
            .newDocumentBuilder()
            .parse(request.getInputStream());
    }
}

ファクトリーの作成から解析までが一つの式にまとまり、途中でファクトリーやパーサーを設定する箇所がありません。ただし、デプロイ全体のJAXP設定でDTDと外部アクセスを拒否していれば、実行時には保護される場合があります。

変更後

java
import java.io.InputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.helpers.DefaultHandler;

public final class SafeXXE {
    static void parse(InputStream xml) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        factory.setFeature(
            "http://apache.org/xml/features/disallow-doctype-decl",
            true
        );

        SAXParser parser = factory.newSAXParser();
        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
        parser.parse(xml, new DefaultHandler());
    }
}

DTDが不要な場合の例です。安全な処理を有効にするだけでなく、DOCTYPE を拒否し、外部DTDとスキーマへのアクセスも明示的に遮断します。

参考資料