XPathインジェクション:XMLの「構造」を悪用する静かなる脅威
「SQLインジェクションはもう警戒している。だからうちは大丈夫だ」
現場で若手エンジニアからよく聞く言葉だ。だが、彼らが盲信しているその「データベースの守り」のすぐ横で、XMLデータベースや設定ファイルがXPathを介して無防備に晒されていることに気づいていない。
XPathインジェクションは、SQLインジェクションの「XML版」だ。しかし、SQLと決定的に違うのは、「XMLは階層構造を持つ」という点にある。攻撃者は、単なるデータの抽出だけでなく、XMLの構造そのものを操作し、認証を無効化したり、隠されたノードを露呈させたりする。今回は、この「盲点」を突く攻撃手法と、現場で明日から使える防御策を叩き込む。
—
1. 攻撃者が狙う「構造の破壊」
攻撃者は、アプリケーションがユーザーからの入力をそのままXPathクエリに結合している場所を探す。例えば、認証機能で以下のクエリが生成されていたとしよう。
//user[username/text()=' + $username + ' and password/text()=' + $password + ']
ここで、攻撃者が username に ' or '1'='1 を入力するとどうなるか。
//user[username/text()='' or '1'='1' and password/text()='...']
結果、評価は常に真となり、パスワードを知らなくても先頭のユーザーでログインが成功する。これはSQLiと同じ手口だが、XPathの恐ろしさはここからだ。count() 関数や substring() を駆使して、XML内の全ノードを1文字ずつ総当たりで抽出する「ブラインドXPathインジェクション」が可能になる。
—
2. 対策の核心:パラメータ化クエリの徹底
初心者がやりがちな「入力値のサニタイズ(シングルクォートのエスケープなど)」は、絶対に信用してはいけない。攻撃者はエンコーディングや特殊なXPath関数を使って容易に回避する。
唯一の正解は、「クエリとユーザー入力を分離する」ことだ。多くの言語では、SQLと同じようにXPathでも「変数のバインディング」がサポートされている。
実装例:PHP (DOMXPath + registerNamespace)
PHPの DOMXPath を使用する場合、以下のように変数バインディングを行うのが鉄則だ。
load(‘users.xml’);
$xpath = new DOMXPath($dom);
// ユーザー入力を直接クエリに連結してはならない
$userInputUsername = $_POST[‘username’];
// query()の第2引数以降を活用し、変数をバインドする
// XPath式の中で $var を使い、第3引数で値を渡すことでインジェクションを物理的に遮断する
$query = “//user[username/text() = \$user]”;
$xpath->registerVariable(‘user’, $userInputUsername);
$result = $xpath->query($query);
if ($result->length > 0) {
echo “認証成功”;
} else {
echo “認証失敗”;
}
?>
実装例:Python (lxml)
Pythonの lxml も同様に、辞書形式でパラメータを渡すことで安全を確保できる。
from lxml import etree
XMLの読み込み
tree = etree.parse(“users.xml”)
ユーザー入力
user_input = “admin’ or ‘1’=’1”
XPathの変数を定義してバインドする
これにより、入力値は文字列リテラルとしてのみ扱われ、クエリの構造を壊すことはできない
result = tree.xpath(“//user[username/text() = $name]”, name=user_input)
if result:
print(“ログイン成功”)
—
3. インフラ層での防御:多層防御の考え方
コードレベルでの修正が完了したとしても、万が一の脆弱性混入に備え、インフラ側でも保険をかけるべきだ。
WAFの活用 (ModSecurity / AWS WAF)
XPathインジェクション特有のキーワード(count(, substring(, string-length(, //, node(), name())を検知するルールをWAFに組み込む。
AWS WAFのカスタムルール例(概念):
Match Type: Regex MatchRegex Pattern:(?i)(count\(|substring\(|name\(|//)Action: Block
ただし、WAFはあくまで「予防線」だ。WAFだけに頼り、アプリケーションコードの脆弱性を放置するような設計は、プロのエンジニアとして恥じるべき行為である。
—
最後に:なぜ「動く」だけではダメなのか
私のキャリアの中で、最も深刻なインシデントは常に「動いているから大丈夫」という慢心から発生してきた。XPathインジェクションは、一度成功すればXML内の機密情報(設定ファイルやユーザーリスト、場合によっては暗号化鍵そのもの)が丸裸になる。
あなたが書いたそのコード、" や ' を入力してエラーが返ってこないか? もしエラーが出るなら、それは脆弱性のサインだ。
コードを書く時は常に「入力値は悪意ある攻撃者からの手紙である」と心得ること。今日紹介したパラメータ化の実装を、今すぐあなたのプロジェクトに適用してほしい。それがシステムを守る、最初の、そして最大の防壁になる。
—
執筆者:セキュリティチーフエンジニア
「技術はツールに過ぎない。それを使いこなす者の意識こそが、最強のセキュリティだ。」
コメント