【実務・中級編】XPathインジェクションによるXMLデータ構造の破壊と抽出 – アプリケーションセキュリティ & 安全な開発防御ガイド

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 Match
  • Regex Pattern: (?i)(count\(|substring\(|name\(|//)
  • Action: Block

ただし、WAFはあくまで「予防線」だ。WAFだけに頼り、アプリケーションコードの脆弱性を放置するような設計は、プロのエンジニアとして恥じるべき行為である。

—

最後に:なぜ「動く」だけではダメなのか

私のキャリアの中で、最も深刻なインシデントは常に「動いているから大丈夫」という慢心から発生してきた。XPathインジェクションは、一度成功すればXML内の機密情報(設定ファイルやユーザーリスト、場合によっては暗号化鍵そのもの)が丸裸になる。

あなたが書いたそのコード、" や ' を入力してエラーが返ってこないか? もしエラーが出るなら、それは脆弱性のサインだ。

コードを書く時は常に「入力値は悪意ある攻撃者からの手紙である」と心得ること。今日紹介したパラメータ化の実装を、今すぐあなたのプロジェクトに適用してほしい。それがシステムを守る、最初の、そして最大の防壁になる。

—
執筆者:セキュリティチーフエンジニア
「技術はツールに過ぎない。それを使いこなす者の意識こそが、最強のセキュリティだ。」

コメント

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