説明
信頼できない文字列がデータではなくXPathの構文として解釈されると、XPathインジェクションが発生します。引用符、論理演算子、パス演算子、関数などを挿入され、条件が常に真になったり、意図しないノードが選ばれたりする可能性があります。
XPathExpression.Compileは式を事前に解析するだけで、信頼性を保証しません。入力から組み立てた式をコンパイルして再利用しても、問題は残ります。XmlNamespaceManagerも接頭辞と名前空間を解決するもので、クエリ変数をバインドしません。
XMLのマークアップ構造を変えるXMLインジェクションや、外部エンティティでファイルやネットワーク資源にアクセスするXXEとは別の問題です。
想定される影響
- 認証や認可の条件を回避される可能性があります。
- 権限のないXMLノードや属性を取得される可能性があります。
- エラー応答や繰り返しのクエリから、XMLの構造やデータを推測される可能性があります。
- 複雑な式を指定されると、CPU使用量や応答時間が増える可能性があります。
対処方法
XPathの構造は開発者が管理し、信頼できない値を構文から分離してください。
- 任意の文字列は、固定した式の変数としてバインドしてください。.NETでは独自の
XsltContextとIXsltContextVariableを実装し、XPathExpression.SetContextで設定できます。 - 数値など範囲が明確な値は、形式と範囲を厳密に解析し、正規化した値を適切なXPathの位置に使ってください。文字列の位置に数値の検証を流用しないでください。
- クエリを選ばせる場合は、トークンを開発者が作った固定のXPathに対応付けてください。入力自体を式として渡さないでください。
- 完成した式を受け取る必要がある場合は、変更不可のローカルなリテラル一覧との完全一致を求めてください。一覧の内容と変更権限をアプリケーションが管理します。
XML・HTMLエンコーダー、SecurityElement.Escape、引用符の置換、XmlNamespaceManager、名前がSanitizeというだけのヘルパーは、汎用的なXPathのパラメーター化ではありません。文字列リテラル、数値、名前、パスでは文法が異なるため、任意の式を組み立てないことを優先してください。
例
変更前
using Microsoft.AspNetCore.Mvc;
using System.Xml;
XmlNode? FindUser(XmlDocument document, [FromQuery] string name)
{
var expression = "//users/user[@name='" + name + "']";
return document.SelectSingleNode(expression);
}
nameが' or '1'='1などの値なら、条件の構造が変わります。
変更後
文字列を変数としてバインド
固定の$name変数を独自のXsltContextで解決します。入力値はXPathExpression.Compileに渡しません。
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
sealed class VariableContext : XsltContext
{
private readonly IReadOnlyDictionary<string, string> values;
public VariableContext(
NameTable nameTable,
IReadOnlyDictionary<string, string> values) : base(nameTable)
{
this.values = values;
}
public override bool Whitespace => true;
public override int CompareDocument(string baseUri, string nextBaseUri) =>
StringComparer.Ordinal.Compare(baseUri, nextBaseUri);
public override bool PreserveWhitespace(XPathNavigator node) => true;
public override IXsltContextFunction ResolveFunction(
string prefix,
string name,
XPathResultType[] argumentTypes) =>
throw new XPathException($"Unknown function: {prefix}:{name}");
public override IXsltContextVariable ResolveVariable(
string prefix,
string name)
{
if (prefix.Length == 0 && values.TryGetValue(name, out var value))
{
return new BoundString(value);
}
throw new XPathException($"Unknown variable: {prefix}:{name}");
}
}
sealed class BoundString : IXsltContextVariable
{
private readonly string value;
public BoundString(string value) => this.value = value;
public bool IsLocal => false;
public bool IsParam => true;
public XPathResultType VariableType => XPathResultType.String;
public object Evaluate(XsltContext context) => value;
}
sealed class Search
{
public XPathNavigator? FindUser(XPathNavigator navigator, [FromQuery] string name)
{
var expression = XPathExpression.Compile("//users/user[@name=$name]");
var values = new Dictionary<string, string> { ["name"] = name };
expression.SetContext(new VariableContext(new NameTable(), values));
return navigator.SelectSingleNode(expression);
}
}
必要な変数名と型だけを許可し、未知の変数や関数は拒否してください。名前空間の接頭辞が必要なら、同じコンテキストに固定の対応付けを追加します。
数値の範囲を厳密に解析
using Microsoft.AspNetCore.Mvc;
using System.Globalization;
using System.Xml;
XmlNode? FindUser(XmlDocument document, [FromQuery] string id)
{
if (!int.TryParse(
id,
NumberStyles.None,
CultureInfo.InvariantCulture,
out var numericId))
{
return null;
}
return document.SelectSingleNode($"//users/user[@id={numericId}]");
}
符号や空白のない10進整数だけを受け付け、正規化したintを引用符のない数値の位置に使います。業務上の許容範囲は別途確認してください。
選択トークンを固定のXPathに対応付け
using Microsoft.AspNetCore.Mvc;
using System.Xml;
XmlNode? FindView(XmlDocument document, [FromQuery] string view)
{
string? expression = view switch
{
"active" => "//users/user[@active='true']",
"admin" => "//users/user[@role='admin']",
_ => null
};
return expression is null
? null
: document.SelectSingleNode(expression);
}
XPathの構文を保護しても、取得結果に対する認証・認可、XXE対策、XML構造の保護は別途必要です。
関連基準
- CWE: CWE-643: XPath式内のデータの無害化不備(CWE 4.20)
- OWASP Top 10: A05:2025 Injection, A03:2021 Injection
- OWASP ASVS: v5.0.0-1.2.7
参考資料
- .NETと.NET Coreの公式サポート方針
- Microsoft Learn: CA3008 — XPathインジェクションの確認
- Microsoft Learn: XmlNode.SelectNodes
- Microsoft Learn: XPathNavigator
- Microsoft Learn: XPathExpression.Compile
- Microsoft Learn: XPathExpression.SetContext
- Microsoft Learn: ユーザー定義のXPath関数と変数
- Microsoft Learn: LINQ to XML XPath Extensions
- CodeQL: C#のXPathインジェクション
- ソフトウェアセキュリティ弱点診断ガイド2021
- 重要情報通信基盤施設の脆弱性分析・評価基準