【実務・中級編】XPathインジェクションの仕組みとXMLデータストアの保護 – アプリケーションセキュリティ & 安全な開発防御ガイド

XPathインジェクション:XMLの「構造」が牙を剥く瞬間

やあ。現場で戦うエンジニア諸君。今日は、多くの開発者が「XMLなんて古い技術だろ?」と油断している隙を突く、XPathインジェクションについて話そう。

REST APIが主流の現代でも、レガシーなエンタープライズシステムや、設定ファイルをXMLで管理するツールは依然として多い。そして、そうしたシステムで最も恐ろしいのは、「ユーザーからの入力をそのままクエリに埋め込む」という、Web黎明期からの悪しき伝統が生き残っていることだ。

1. XPathインジェクションの「盲点」:論理を書き換える快感

SQLインジェクションが「データベースのテーブルを破壊する」ものだとしたら、XPathインジェクションは「XMLツリーの構造を悪用して、認可の壁をすり抜ける」ものだ。

例えば、ユーザー名とパスワードをXMLファイルで管理しているとする。




admin secret123

ここで、検索クエリが以下のようになっているとしよう。
//user[username/text()='$user_input' and password/text()='$pass_input']

攻撃者が $user_input に ' or '1'='1 を入力したらどうなる?
クエリは //user[username/text()='' or '1'='1' and ...] となり、最初のユーザー(多くの場合管理者)をいとも簡単に特定できてしまう。これが、攻撃者の最初の「足掛かり」だ。

2. なぜ「サニタイズ」だけでは不十分なのか

多くの若手エンジニアは、str_replace や正規表現で ' や " をエスケープしようとする。だが、これは「戦場に竹槍で挑む」のと同義だ。

XMLは構造化データであり、エスケープ漏れ一つでツリーの階層を飛び越えられる。例えば、ancestor:: や following-sibling:: といった軸指定を使えば、認証をすり抜けるだけでなく、システム内の全データを総なめにすることも可能だ。

対策の原則はただ一つ:「クエリに直接、ユーザー入力を混ぜるな」。

3. 実践:パラメータ化された安全なクエリ

現代のライブラリには、SQLのプリペアドステートメントのように、変数を安全にバインドする機能が備わっている。これを正しく使おう。

Python (lxml) を使用したセキュアな実装例

Pythonの lxml を使う場合、変数を直接文字列結合してはいけない。xpath 関数の variables 引数を使うのが鉄則だ。

from lxml import etree

def get_user_data(xml_data, username_input):
root = etree.fromstring(xml_data)

# 【重要】直接クエリに埋め込まず、変数バインドを使う
# $user という変数を定義し、そこに値を安全に渡す
query = “//user[username=$user]”

# 第2引数に辞書形式で変数をマッピングする
results = root.xpath(query, user=username_input)

if results:
return “ユーザー発見”
return “アクセス拒否”

攻撃者が ‘ or ‘1’=’1 を入力しても、これなら安全に処理される

PHP (DOMXPath) の場合

PHPには残念ながら直接的な「パラメータバインド」機能が標準のDOMXPathには存在しない。だからこそ、外部からの入力は徹底的にバリデーション(ホワイトリスト方式)を行う必要がある。

function get_user_xml($dom, $username) {
// 1. バリデーション: 英数字以外は徹底的に排除する
if (!preg_match(‘/^[a-zA-Z0-9]+$/’, $username)) {
throw new Exception(“不正な入力です。”);
}

$xpath = new DOMXPath($dom);
// 2. 構造破壊を防ぐため、シングルクォートを適切に処理する(可能ならクエリ構築自体を避ける)
$query = sprintf(“//user[username=’%s’]”, $xpath->quote($username));

return $xpath->query($query);
}

4. インフラ層で守る「最後の砦」

アプリケーションの修正が追いつかない場合、あるいは多層防御を強固にしたい場合は、WAFでのブロックが有効だ。

Nginx/ModSecurity (OWASP Core Rule Set) を使用しているなら、以下のシグネチャを意識してほしい。

ModSecurityのカスタムルール例(概念)
XPathの攻撃で頻出する文字列を検知して遮断する
SecRule ARGS “@rx (?i)(ancestor|following-sibling|namespace|//|\[.=.\])” \
“id:1000001,phase:2,deny,status:403,msg:’XPath Injection Detected'”

チーフからの助言

脆弱性を見つけるのは楽しいが、それを「塞ぎ続ける」のは地味で泥臭い作業だ。しかし、この地道な作業こそが、夜中に電話で叩き起こされる回数を劇的に減らす。

1. ライブラリの仕様を疑え:自分の使っているライブラリが「パラメータ化」をサポートしているか、公式ドキュメントを読み込め。
2. ホワイトリストを愛せ:入力値が期待通りの型か、文字種か。これだけで9割の攻撃は防げる。
3. XMLの必要性を再考せよ:可能ならJSONや他の構造化データへの移行を検討してくれ。技術的負債を解消することも、我々エンジニアの重要な仕事だ。

セキュリティは「魔法」ではない。論理的な積み重ねだ。今日のコードが、君のシステムを守る盾になることを願っている。何か不明な点があれば、いつでも共有してくれ。

コメント

タイトルとURLをコピーしました