説明
XPath式には、XMLノードを選ぶパスだけでなく、条件式、関数、演算子も含まれます。リクエスト文字列を compile、evaluate、evaluateExpression、dom4jの選択メソッド、Jaxenの式コンストラクターの式を指定する引数へ連結したり、そのまま渡したりすると、攻撃者がデータ値ではなくクエリ構造を選択できるおそれがあります。
式とデータを区別してください。xpath.evaluate(expression, document) の第1引数は式、第2引数は検索対象のコンテキストです。外部の値は固定の式の変数として宣言し、XPathVariableResolver またはdom4j/Jaxenの VariableContext で渡してください。
XPath 1.0の文字列リテラルには、任意の入力に使える汎用的なエスケープ構文はありません。引用符の除去、一般的な escape・sanitize ヘルパー、形式の正規表現、事前コンパイルだけでは、データと式の構造を分離できません。
想定される影響
- 許可されていないXMLデータの取得: パスや条件式を変更され、意図したフィルターの外にあるノードを選択されるおそれがあります。
- セキュリティチェックの回避: XPathの結果を認証、認可、ポリシーの判断に使っている場合、その判断を操作される可能性があります。
- リソースの消費: 式全体を入力できる機能では、複雑なパスや演算によって評価の負荷が増える場合があります。XPathの使用自体が、必ずサービス拒否につながるわけではありません。
対処方法
基本は、XPath式をサーバー側で定義し、外部入力はデータ値としてのみバインドすることです。
- Java XPathでは固定の式に
$name変数を使い、XPath#setXPathVariableResolverで値を渡してください。リゾルバーは式のコンパイルや評価の前に設定してください。 - dom4jとJaxenでは、固定の式と
VariableContextを組み合わせてください。 - 要素名、軸、条件式の一部など、変数で表せない構造をユーザーに選ばせる場合は、外部のキーを少数の完全なサーバー定義の式に対応付けてください。入力文字列自体を連結しないでください。
- 入力検証は長さや識別子形式などの業務上の制約に使い、変数バインドの代わりにしないでください。
- 信頼できない式を組み立ててから
compileしても対策にはなりません。コンパイル自体が文字列をXPath構文として解釈します。
例
Java XPath
変更前
リクエスト値を引用符の間に連結するため、profileId が条件式の構造を変える可能性があります。
import jakarta.servlet.http.HttpServletRequest;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
final class VulnerableXPathLookup {
static Node findProfile(HttpServletRequest request, Document document)
throws XPathExpressionException {
String profileId = request.getParameter("profileId");
String expression = "/directory/profile[@id='" + profileId + "']";
XPath xpath = XPathFactory.newInstance().newXPath();
return xpath.evaluateExpression(expression, document, Node.class);
}
}
変更後
式を定数にし、リクエスト値を変数としてバインドします。引用符やXPathのトークンを含む値も、式の構文ではなく変数の値として扱われます。
import jakarta.servlet.http.HttpServletRequest;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
final class SafeXPathLookup {
static Node findProfile(HttpServletRequest request, Document document)
throws XPathExpressionException {
String profileId = request.getParameter("profileId");
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setXPathVariableResolver(variable -> switch (variable.getLocalPart()) {
case "profileId" -> profileId;
default -> null;
});
String expression = "/directory/profile[@id=$profileId]";
return xpath.evaluateExpression(expression, document, Node.class);
}
}
XPath はスレッドセーフでも再入可能でもありません。共有する XPath オブジェクトのリゾルバーをリクエストごとに書き換えないでください。例のようにリクエスト単位で使用するか、アプリケーションで同時アクセスを安全に管理してください。
dom4j/Jaxenの変数バインド
dom4jでは、XPathの作成時にJaxenの VariableContext を渡せます。
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.jaxen.SimpleVariableContext;
final class SafeDom4jXPathLookup {
static Node findProfile(Document document, String profileId) {
SimpleVariableContext variables = new SimpleVariableContext();
variables.setVariableValue("profileId", profileId);
XPath xpath = DocumentHelper.createXPath(
"/directory/profile[@id=$profileId]", variables);
return xpath.selectSingleNode(document);
}
}
サーバーが定義した式の選択
XPath変数はデータ値を表し、要素名や軸などの構文は置き換えられません。そのような構造を選択する場合は、入力を検証して連結するのではなく、サーバーが管理する完全な式から選んでください。
final class ServerOwnedXPathView {
static String expressionFor(String requestedView) {
return switch (requestedView) {
case "active" -> "/directory/profile[@active='true']";
case "locked" -> "/directory/profile[@locked='true']";
default -> "/directory/profile";
};
}
}
エンジンに渡るのは、コードで定義した三つの式のいずれかです。requestedView 自体はXPath文字列に含まれません。
任意のXPath機能とリソース制限
管理者向け診断ツールなどで式全体の入力が必要な場合、一般的な無害化で任意の式を安全にすることはできません。機能を分離して厳格に認可し、必要なXMLデータだけを渡し、リクエスト頻度と結果サイズを制限してください。
Java SE 25の jdk.xml.xpathExprGrpLimit と jdk.xml.xpathExprOpLimit は、XPathのグループ数と演算子数を制限します。アプリケーションの要件に合わせて設定してください。これらはリソース消費を抑える多層防御であり、式のインジェクションを解消するものではありません。
2026年9月1日に確認した安定版は、dom4j 2.2.0とJaxen 2.0.6です。Jaxen 2.0.5はコアパーサーの再帰処理を反復アルゴリズムへ変更し、残るスタックオーバーフローを JaxenException として処理するようにしました。2.0.6はそのアルゴリズムの括弧式の評価を修正しています。依存ライブラリの更新は重要ですが、攻撃者が選ぶ式の意味を制限するものではありません。
参考資料
- Java SE 25
XPathAPI - Java SE 25
java.xmlモジュールとJAXP処理制限 - Oracle Java SE 25 Security Developer's Guide
- dom4j 2.2.0 リリース
- dom4j 2.2.0
XPathインターフェース - Jaxen 2.0.6 リリース
- Jaxen 公式リリース履歴
- CWE-643: Improper Neutralization of Data within XPath Expressions
- OWASP Top 10:2025 A05 Injection
- OWASP Top 10:2021 A03 Injection
- OWASP ASVS 5.0.0 V1.2.4