前回はPostgreSQLを例に,データベースシステムでXPathがどのように利用できるか解説しました。PHP本体にもXML機能が組み込まれています。今回はPHP本体のXML機能を使ってXPathの利用方法を解説します。XPathインジェクションの解説は次回行います。今しばらくお待ちください。
PHPのXPath機能
PHP 5.2のDomモジュールはlibxml2のラッパー関数になっています。libxml2はSAX,DOM APIなどXML処理に必要なAPIを提供し,XPath機能もその一つとして提供しています。
次回のXPathインジェクションの解説に利用するので,ユーザ情報を管理するXML文書を用意します。
account.xml
<?xml versoin="1.0" encoding="UTF-8" ?>
<account>
<user id="1">
<name>user1</name>
<password>password1</password>
</user>
<user id="2">
<name>user2</name>
<password>password2</password>
</user>
<user id="3">
<name>user3</name>
<password>password3</password>
</user>
<user id="4">
<name>user4</name>
<password>password4</password>
</user>
</account>
DOMXPathの利用方法
DOMXPathはオブジェクトとして実装されています。DOMオブジェクトを引数にコンストラクタを呼び出し,XPathクエリによりノードを取得します。ノードはDOMElementオブジェクトとして返され,DOMElementメソッドを利用して内容を参照したり変更できるようになります。
簡単なXPathの実行
xpath1.php
<?php
$doc = new DOMDocument;
$doc->load('account.xml');
$xpath = new DOMXPath($doc);
//XPathでaccount文書のすべてのノードを取得
$nodes = $xpath->query('/account/*');
//$nodesはDOMElement。$nもDOMElement
foreach ($nodes as $n) {
var_dump($n);
}
?>
実行結果
object(DOMElement)#3 (0) {
}
object(DOMElement)#4 (0) {
}
object(DOMElement)#5 (0) {
}
object(DOMElement)#6 (0) {
}
account.xmlは4つのuserノードを持つので,4つのDOMElementオブジェクトがvar_dumpにより出力されます。内容を出力するにはDOMElementオブジェクトからノードを取り出し,そのノードの内容を出力します。
XML文書をユーザ認証データベースとして利用
account.xmlを利用してWebアプリケーションの認証システムを作ることができます。先ほど利用したxpath1.phpのXPathクエリの部分を変えるだけで認証に利用できるようになります。
XPathもSQLと同様に比較や結合,論理演算などの演算子をサポートしています。これを利用すると比較的SQLに近い形で認証データベースを利用できます。
xpath2.php
<?php
$doc = new DOMDocument;
$doc->load('account.xml');
$xpath = new DOMXPath($doc);
//ユーザ名が"user1"でパスワードが"password1"であるノードを取り出す
$nodelist = $xpath->query('/account/user[name="user1" and password="password1"]');
var_dump($nodelist->length, $nodelist->item(0)->nodeValue);
?>
account.xmlには該当するノードがあるので以下のような出力となります。
出力結果
[yohgaki@dev tmp]$ php xpath.php int(1) string(22) " user1 password1 "
ユーザ名かパスワードが一致しなければ結果はないので次のようになります。
出力例
[yohgaki@dev tmp]$ php xpath.php Notice: Trying to get property of non-object in /home/yohgaki/tmp/xpath.php on line 10 int(0) NULL
このようにXML文書を利用したユーザ管理システムは比較的簡単に実装できることが分かります。ここではコマンドラインから利用する認証を考えてみます。
$ php auth.php USERNAME PASSWORD
と入力し,USERNAMEとパスワードが一致すると“OK to login”を出力し,一致しない場合は“Not OK to login”を表示するプログラムは以下のようになります。
auth.php
<?php
$doc = new DOMDocument;
$doc->load('account.xml');
$xpath = new DOMXPath($doc);
$nodelist = $xpath->query('/account/user[name="'.$argv[1].'" and password="'.$argv[2].'"]');
if ($nodelist->length) {
echo "OK to login".PHP_EOL;
} else {
echo "Not OK to login".PHP_EOL;
}
?>
警告:あえて脆弱性があるコードになっています。絶体にこのようなコードで認証しないでください。
試しに実行してみると,一応期待どおりと思われるような動作になっていることが確認できます。
実行例
[yohgaki@dev tmp]$ php auth.php user1 password1 OK to login [yohgaki@dev tmp]$ php auth.php user1 password2 Not OK to login
まとめ
筆者の好みとしてはDOMElementやDOMNodeオブジェクトとしてではなく,配列として取得できるオプションが欲しいところです。PHP本体のDOMモジュールを利用すると比較的簡単にXML文書の中から必要なノードだけを抽出できることが分かります。XPathの演算子の利用方法もなんとなくSQLと似ていることも分かったと思います。
XMLでデータ管理を行うと,ほかのシステムとのデータ交換が容易になります。しかし,DOMを利用した場合はドキュメント全体を読み込む必要があるので,大きなドキュメントの読み込みには大量のメモリが必要となるなど,注意しなければならないこともあります。
お待たせしました。次回はいよいよXPathインジェクションと対策の解説です。

