名前 | 説明 |
---|---|
ExtractValue() (v5.1.5) |
XPath 記法を使って XML 文字列から値を抽出します |
UpdateXML() (v5.1.5) |
置換後の XML フラグメントを返します |
この節では MySQL での XML と関連する機能について説明します。
--xml
オプションで呼び出して、mysql
および mysqldump
クライアントの XML フォーマットの出力を MySQL
から得ることは可能です。項3.1. 「mysql — MySQL コマンドラインツール」、項3.4. 「mysqldump — データベースバックアッププログラム」
を参照してください。
MySQL 5.1.5 からは、基礎的な XPath 1.0 (XML Path Language, version 1.0) 機能を提供する 2 つの関数を利用することができます。この節のあとのほうで XPath の構文や使用方法に関する基本情報をいくつか提供しますが、そうしたトピックの詳しい解説はこのドキュメントでは行いません。正式な情報としては、「XML Path Language (XPath) 1.0 標準」を参照するようにしてください。XPath をご存知ない方、基本を復習したい方は Zvon.org XPath Tutorial を参照してください。複数の言語でご覧いただけます。
これらの関数の開発はまだ継続中です。MySQL 5.1 と今後のためにも、これらの関数や XML および XPath の機能を改良し続けていきます。これらに関する議論や質問を行ったり、ほかのユーザーからの助言を得たりするには、MySQL XML ユーザーフォーラムを利用してください。
MySQL 5.1.20 以降では、これらの関数で使用される XPath 式は、ユーザー変数とストアドプログラムの局所変数をサポートしています。ユーザー変数の確認は簡単に、ストアドプログラムの局所変数の確認は詳細に、それぞれ行われます (Bug#26518 も参照)。
ユーザー変数 (簡易確認).
構文
$@
を使用する変数 (つまり、ユーザー変数)
では確認が行われません。変数の型が間違っていたり、変数に値がまだ代入されていなかったりしても、サーバーから警告やエラーは一切発行されません。このことは、タイプミスの責任はすべてユーザーが負うことも意味します。variable_name
$@myvariable
のつもりで
$@myvairable
と入力してしまっても、何の警告も発行されないからです。
例.
mysql>SET @xml = '<a><b>X</b><b>Y</b></a>';
Query OK, 0 rows affected (0.00 sec) mysql>SET @i =1, @j = 2;
Query OK, 0 rows affected (0.00 sec) mysql>SELECT @i, ExtractValue(@xml, '//b[$@i]');
+------+--------------------------------+ | @i | ExtractValue(@xml, '//b[$@i]') | +------+--------------------------------+ | 1 | X | +------+--------------------------------+ 1 row in set (0.00 sec) mysql>SELECT @j, ExtractValue(@xml, '//b[$@j]');
+------+--------------------------------+ | @j | ExtractValue(@xml, '//b[$@j]') | +------+--------------------------------+ | 2 | Y | +------+--------------------------------+ 1 row in set (0.00 sec) mysql>SELECT @k, ExtractValue(@xml, '//b[$@k]');
+------+--------------------------------+ | @k | ExtractValue(@xml, '//b[$@k]') | +------+--------------------------------+ | NULL | | +------+--------------------------------+ 1 row in set (0.00 sec)
ストアドプログラム内の変数 (詳細確認).
これらの関数をストアドプログラム内から呼び出す場合には、構文
$
を使用する変数を宣言し、これらの関数で使用することができます。そのような変数は定義元となるストアドプログラムの局所変数になりますが、それらの変数では型や値に関する詳細確認が行われます。
variable_name
例.
mysql>DELIMITER |
mysql>CREATE PROCEDURE myproc ()
->BEGIN
->DECLARE i INT DEFAULT 1;
->DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>';
-> ->WHILE i < 4 DO
->SELECT xml, i, ExtractValue(xml, '//a[$i]');
->SET i = i+1;
->END WHILE;
->END |
Query OK, 0 rows affected (0.01 sec) mysql>DELIMITER ;
mysql>CALL myproc;
+--------------------------+---+------------------------------+ | xml | i | ExtractValue(xml, '//a[$i]') | +--------------------------+---+------------------------------+ | <a>X</a><a>Y</a><a>Z</a> | 1 | X | +--------------------------+---+------------------------------+ 1 row in set (0.00 sec) +--------------------------+---+------------------------------+ | xml | i | ExtractValue(xml, '//a[$i]') | +--------------------------+---+------------------------------+ | <a>X</a><a>Y</a><a>Z</a> | 2 | Y | +--------------------------+---+------------------------------+ 1 row in set (0.01 sec) +--------------------------+---+------------------------------+ | xml | i | ExtractValue(xml, '//a[$i]') | +--------------------------+---+------------------------------+ | <a>X</a><a>Y</a><a>Z</a> | 3 | Z | +--------------------------+---+------------------------------+ 1 row in set (0.01 sec)
パラメータ. ストアドルーチン内の XPath 式で使用される変数のうち、パラメータとして渡されたものも、詳細確認の対象となります。
ユーザー変数やストアドプログラムの局所変数を含む式は、その他の点 (記法は除く) では、XPath 1.0 仕様で規定されている「変数を含む XPath 式の規則」に準拠する必要があります。
現時点では、XPath 式の格納に使用されたユーザー変数は、空の文字列とみなされます。このため、ユーザー変数に XPath 式を格納することはできません。この問題は今後の MySQL リリースで修正される予定です。(Bug#32911)
ExtractValue(
xml_frag
,
xpath_expr
)
ExtractValue()
はふたつの文字列引数、XML
マークアップのフラグメント
xml_frag
、そして XPath 式
xpath_expr
(locator とも呼ばれる)
を取り、XPath
式によってマッチされた要素の子である、最初のテキストノードのテキスト
(CDATA
)
を返します。これは、/text()
を付加したあとに、xpath_expr
を使用してマッチを行うのと同様です。つまり、ExtractValue('<a><b>Sakila</b></a>',
'/a/b')
と
ExtractValue('<a><b>Sakila</b></a>',
'/a/b/text()')
は同じ結果を生成します。
複数のマッチが検出される場合、各マッチングエレメントの、最初の子のテキストノードの内容は、単一の、スペースで区切られた文字列として (マッチした順で) 戻されます。
式 (暗黙の /text()
も含む) にマッチするテキストノードが 1
件も見つからない場合にはその理由が何であれ、xpath_expr
が有効でかつ、ネスト化やクローズが正しく行われた要素から
xml_frag
が構成されているかぎり、空の文字列が返されます。空の要素での整合と、整合するものがないのとは、区別はされません。これはデザインによるものです。
xml_frag
でマッチする要素が見つからなかったのか、またはマッチする要素はあったものの、非子テキストノードを含んでいたのかを判断する必要があれば、XPath
count()
関数を使用する式の結果をテストしてください。たとえば、次のように、これらのステートメントの両方が空の文字列を返す場合
:
mysql>SELECT ExtractValue('<a><b/></a>', '/a/b');
+-------------------------------------+ | ExtractValue('<a><b/></a>', '/a/b') | +-------------------------------------+ | | +-------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue('<a><c/></a>', '/a/b');
+-------------------------------------+ | ExtractValue('<a><c/></a>', '/a/b') | +-------------------------------------+ | | +-------------------------------------+ 1 row in set (0.00 sec)
しかし、次のように、実際にまっちする要素があったのかを確認することはできます :
mysql>SELECT ExtractValue('<a><b/></a>', 'count(/a/b)');
+-------------------------------------+ | ExtractValue('<a><b/></a>', 'count(/a/b)') | +-------------------------------------+ | 1 | +-------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue('<a><c/></a>', 'count(/a/b)');
+-------------------------------------+ | ExtractValue('<a><c/></a>', 'count(/a/b)') | +-------------------------------------+ | 0 | +-------------------------------------+ 1 row in set (0.01 sec)
ExtractValue()
は CDATA
のみを返し、マッチングタグに含まれるタグや、それらのコンテントは戻されません
(次の例の、val1
として戻された結果を参照) 。
mysql>SELECT
->ExtractValue('<a>ccc<b>ddd</b></a>', '/a') AS val1,
->ExtractValue('<a>ccc<b>ddd</b></a>', '/a/b') AS val2,
->ExtractValue('<a>ccc<b>ddd</b></a>', '//b') AS val3,
->ExtractValue('<a>ccc<b>ddd</b></a>', '/b') AS val4,
->ExtractValue('<a>ccc<b>ddd</b><b>eee</b></a>', '//b') AS val5;
+------+------+------+------+---------+ | val1 | val2 | val3 | val4 | val5 | +------+------+------+------+---------+ | ccc | ddd | ddd | | ddd eee | +------+------+------+------+---------+
MySQL 5.1.8 以降では、この関数は
contains()
による比較を行う際に現在の SQL
照合を使用しますが、引数の照合強制性を考慮するという点で、ほかの文字列関数
(CONCAT()
など)
と同じ照合集約を実行します。この動作を決定する規則の説明については、Special Cases Where Collation Determination Is Trickyを参照してください。
(以前はバイナリ比較、つまり大文字小文字の区別がある比較が常に使用されていました)。
MySQL 5.1.12
以降では、ネスト化やクローズが正しく行われていない要素が
xml_frag
に含まれている場合には、NULL
が返され、警告が生成されます。次に例を示します。
mysql>SELECT ExtractValue('<a>c</a><b', '//a');
+-----------------------------------+ | ExtractValue('<a>c</a><b', '//a') | +-----------------------------------+ | NULL | +-----------------------------------+ 1 row in set, 1 warning (0.00 sec) mysql>SHOW WARNINGS;
+---------+------+-------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------+ | Warning | 1523 | Incorrect XML value: 'parse error at line 1 pos 11: END-OF-INPUT unexpected ('>' wanted)' | +---------+------+-------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue('<a>c</a><b/>', '//a');
+-------------------------------------+ | ExtractValue('<a>c</a><b/>', '//a') | +-------------------------------------+ | c | +-------------------------------------+ 1 row in set (0.00 sec)
MySQL 5.1.12 より前は、そのような場合に空の文字列が返されていました。(Bug#18201)
UpdateXML(
xml_target
,
xpath_expr
,
new_xml
)
この関数は、XML マークアップ
xml_target
の提示されたフラグメントの単一部を、新しい
XML フラグメント new_xml
に置き換え、その後チャージされた XML
を返します。置換された
xml_target
の一部は、ユーザーから提供された XPath 式
xpath_expr
にマッチします。xpath_expr
にマッチする式が検出されない場合、または複数のマッチが見つかった場合、この関数は独自の
xml_target
XML
フラグメントを返します。3
つの引数すべてが文字列であるべきです。
mysql>SELECT
->UpdateXML('<a><b>ccc</b><d></d></a>', '/a', '<e>fff</e>') AS val1,
->UpdateXML('<a><b>ccc</b><d></d></a>', '/b', '<e>fff</e>') AS val2,
->UpdateXML('<a><b>ccc</b><d></d></a>', '//b', '<e>fff</e>') AS val3,
->UpdateXML('<a><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val4,
->UpdateXML('<a><d></d><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val5
->\G
*************************** 1. row *************************** val1: <e>fff</e> val2: <a><b>ccc</b><d></d></a> val3: <a><e>fff</e><d></d></a> val4: <a><b>ccc</b><e>fff</e></a> val5: <a><d></d><b>ccc</b><d></d></a>
XPath シンタックスのさらに詳しい説明や使用方法は、このドキュメントの対象スコープではありません。正式な情報としては、「XML Path Language (XPath) 1.0 仕様」を参照してください。XPath をご存知ない方、基本を復習したい方は Zvon.org XPath Tutorial を参照してください。複数の言語でご覧いただけます。
次は、いくつかの基本的な XPath 式の説明と例です :
/
tag
<
がルートの要素である場合にのみ、tag
/><
にマッチします。
tag
/>
例 :/a
は、最外部の
(ルート)
タグとマッチするため、<a><b/></a>
に一致を持ちます。このインスタンスではほかの要素の子になるため、<b><a/></b>
の内側 a
要素とは一致しません。
/
tag1
/tag2
<
の子と、tag1
/><
がルートの要素である場合にのみ、tag1
/><
にマッチします。
tag2
/>
例 :ルートの要素 a
の子であるため、/a/b
は XML フラグメント
<a><b/></a>
内の b
要素とマッチします。このケースでは
b
はルートの要素
(従ってほかのどの要素の子でもない)
ため、<b><a/></b>
でマッチするものはありません。XPath
式もまた、<a><c><b/></c></a>
でマッチするものはありません。従って、b
は a
の子孫ですが、a
の子ではありません。
この構築は 3
つ以上の要素に拡張可能です。たとえば、XPath
式 /a/b/c
は、フラグメント
<a><b><c/></b></a>
の c
要素に一致します。
//
tag
<
の任意のインスタンスにマッチします。
tag
>
例 ://a
は、次のうちのどの a
要素とも一致します :
<a><b><c/></b></a>
;
<c><a><b/></a></b>
;
<c><b><a/></b></c>
//
は
/
との結合が可能です。たとえば、//a/b
は、フラグメント
<a><b/></a>
または
<a><b><c/></b></a>
のどれの b
要素ともマッチします。
//
は
tag
/descendant-or-self::*/
と同等です。一般的な間違いは、これと
tag
/descendant-or-self::
とを混同することです。後者の式は実際には、次に示すようにまったく異なる結果につながる可能性があります。
tag
mysql>SET @xml = '<a><b><c>w</c><b>x</b><d>y</d>z</b></a>';
Query OK, 0 rows affected (0.00 sec) mysql>SELECT @xml;
+-----------------------------------------+ | @xml | +-----------------------------------------+ | <a><b><c>w</c><b>x</b><d>y</d>z</b></a> | +-----------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue(@xml, '//b[1]');
+------------------------------+ | ExtractValue(@xml, '//b[1]') | +------------------------------+ | x z | +------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue(@xml, '//b[2]');
+------------------------------+ | ExtractValue(@xml, '//b[2]') | +------------------------------+ | | +------------------------------+ 1 row in set (0.01 sec) mysql>SELECT ExtractValue(@xml, '/descendant-or-self::*/b[1]');
+---------------------------------------------------+ | ExtractValue(@xml, '/descendant-or-self::*/b[1]') | +---------------------------------------------------+ | x z | +---------------------------------------------------+ 1 row in set (0.06 sec) mysql>SELECT ExtractValue(@xml, '/descendant-or-self::*/b[2]');
+---------------------------------------------------+ | ExtractValue(@xml, '/descendant-or-self::*/b[2]') | +---------------------------------------------------+ | | +---------------------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue(@xml, '/descendant-or-self::b[1]');
+-------------------------------------------------+ | ExtractValue(@xml, '/descendant-or-self::b[1]') | +-------------------------------------------------+ | z | +-------------------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue(@xml, '/descendant-or-self::b[2]');
+-------------------------------------------------+ | ExtractValue(@xml, '/descendant-or-self::b[2]') | +-------------------------------------------------+ | x | +-------------------------------------------------+ 1 row in set (0.00 sec)
*
演算子は、どの要素ともマッチする
「wildcard」
のように作用します。たとえば、式
/*/b
は、XML
フラグメント
<a><b/></a>
または
<c><b/></c>
のどの b
要素ともマッチします。しかし、b
はほかのどれかの要素の子であるため、この式はフラグメント
<b><a/></b>
ではマッチを生産しません。ワイルドカードはどのポジションででも使用することができます
: 式 /*/b/*
は、それ自身がルートの要素でない
b
要素の、どの子ともマッチします。
|
(UNION
)
演算子を使用すれば、いくつかのロケータのいずれかにマッチさせることができます。たとえば、XPath
式 //b|//c
は、XML
ターゲットのすべての b
および c
要素にマッチします。
その特性のひとつ以上の値に基づいた要素にマッチすることも可能です。これは、構文
を用いて行います。たとえば、XPath 式
tag
[@attribute
="value
"]//b[@id="idB"]
は、フラグメント
<a><b
id="idA"/><c/><b id="idB"/></a>
の 2 番目の b
要素に一致します。
を含む「任意の」要素に一致させるには、XPath
式
attribute
="value
"//*[
を使用します。
attribute
="value
"]
複数の属性値をフィルターにかけるには、単に複数の属性比較句を継続的に使用します。たとえば、XPath
式 //b[@c="x"][@d="y"]
は、与えられた XML
フラグメントの各所で起こっている
<b c="x" d="y"/>
要素に一致します。
同じ属性が複数の値のいずれかとマッチする要素を見つけるには、|
演算子によってつながれた複数のロケータを使う必要があります。たとえば、c
属性が値 23 または 17 を持つ、すべての
b
要素にマッチするには、式
//b[@c="23"]|//b[@c="17"]
を使用します。この目的のために、次のように論理
or
演算子を使用することもできます。//b[@c="23"
or @c="17"]
。
or
と
|
の違いは、or
は条件を連結するのに対し、|
は結果セットを連結するという点にあります。
XPath の制限. これらの関数にサポートされている XPath シンタックスは、現在、以下の制限の対象となっています :
ノードセット間比較
('/a/b[@c=@d]'
など)
はサポートされていません。
MySQL 5.1.14 より前は、等価と非等価
(=
と
!=
)
が、サポートされている唯一の比較演算子でした。MySQL
5.1.14 以降では、標準の XPath
比較演算子がすべてサポートされています。(Bug#22823)
相対ロケータ式の解決は、ルートノードの文脈で行われます。たとえば、次のようなクエリーと結果を考えます。
mysql>SELECT ExtractValue(
->'<a><b c="1">X</b><b c="2">Y</b></a>',
->'a/b'
->) AS result;
+--------+ | result | +--------+ | X Y | +--------+ 1 row in set (0.03 sec)
この場合はロケータ
a/b
が
/a/b
に解決されています。
相対ロケータは述語内でもサポートされます。次の例では、d[../@c="1"]
が /a/b[@c="1"]/d
として解決されています。
mysql>SELECT ExtractValue(
->'<a>
-><b c="1"><d>X</d></b>
-><b c="2"><d>X</d></b>
-></a>',
->'a/b/d[../@c="1"]')
->AS result;
+--------+ | result | +--------+ | X | +--------+ 1 row in set (0.00 sec)
変数参照やリテラル、数値、スカラー関数呼び出しなど、評価結果がスカラー値になる式で始まるロケータは、サポートされていません。MySQL 5.1.32 以降ではそれらは使用できず、使用するとエラーが発生します。(Bug#42495)
次のようなノード型と
::
演算子との組み合わせは、サポートされていません。
axis
::comment()
axis
::text()
axis
::processing-instructions()
axis
::node()
ただし、次の例に示すように、名前のテスト
(
や
axis
::name
など) はサポートされています。
axis
::*
mysql>SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b');
+-------------------------------------------------------+ | ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b') | +-------------------------------------------------------+ | x | +-------------------------------------------------------+ 1 row in set (0.02 sec) mysql>SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*');
+-------------------------------------------------------+ | ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*') | +-------------------------------------------------------+ | x y | +-------------------------------------------------------+ 1 row in set (0.01 sec)
「Up-and-down」 ナビゲーションは、パスがルートエレメントの「上」をリードする場合はサポートされていません。つまり、現在の要素のひとつ以上の祖先が同時にルートエレメントの祖先であり、与えたれた要素の祖先の継承上でマッチする式を使用することができません (Bug#16321 参照) 。
次の XPath 関数はサポートされていないか、説明しているような既知の問題を抱えています。
次の軸はサポートされていません :
following-sibling
following
preceding-sibling
preceding
MySQL 5.1.10 からは、XPath 式は引数として
ExtractValue()
に渡され、UpdateXML()
が、XML
名前空間記号を採用してマークアップとの使用を有効にする要素セレクタに、コロン文字
(「:
」)
を含むこともあります。例 :
mysql>SET @xml = '<a>111<b:c>222<d>333</d><e:f>444</e:f></b:c></a>';
Query OK, 0 rows affected (0.00 sec) mysql>SELECT ExtractValue(@xml, '//e:f');
+-----------------------------+ | ExtractValue(@xml, '//e:f') | +-----------------------------+ | 444 | +-----------------------------+ 1 row in set (0.00 sec) mysql>SELECT UpdateXML(@xml, '//b:c', '<g:h>555</g:h>');
+--------------------------------------------+ | UpdateXML(@xml, '//b:c', '<g:h>555</g:h>') | +--------------------------------------------+ | <a>111<g:h>555</g:h></a> | +--------------------------------------------+ 1 row in set (0.00 sec)
これは Apache
Xalan
とほかのいくつかのパーサーによって利用できるものに似ており、また、名前空間宣言または
namespace-uri()
や
local-name()
関数を要求したりするよりより単純です。
エラー処理.
ExtractValue()
と UpdateXML()
のどちらの場合も、使用する XPath
ロケータが有効であり、かつネスト化やクローズが正しく行われた要素から検索対象の
XML
が構成されている必要があります。ロケータが無効な場合は、次のようなエラーが生成されます。
mysql> SELECT ExtractValue('<a>c</a><b/>',
'/&a');
ERROR 1105 (HY000): XPATH syntax error:
'&a'
ネスト化やクローズが正しく行われた要素から
xml_frag
が構成されていない場合には、NULL
が返され、警告が生成されます。次に例を示します。
mysql>SELECT ExtractValue('<a>c</a><b', '//a');
+-----------------------------------+ | ExtractValue('<a>c</a><b', '//a') | +-----------------------------------+ | NULL | +-----------------------------------+ 1 row in set, 1 warning (0.00 sec) mysql>SHOW WARNINGS;
+---------+------+-------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------+ | Warning | 1523 | Incorrect XML value: 'parse error at line 1 pos 11: END-OF-INPUT unexpected ('>' wanted)' | +---------+------+-------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql>SELECT ExtractValue('<a>c</a><b/>', '//a');
+-------------------------------------+ | ExtractValue('<a>c</a><b/>', '//a') | +-------------------------------------+ | c | +-------------------------------------+ 1 row in set (0.00 sec)
MySQL 5.1.12 より前は、そのような場合に空の文字列が返されていました。(Bug#18201)
UpdateXML()
の第 3 引数として使用される置換用 XML
が、ネスト化やクローズが正しく行われた要素のみから構成されているかどうかの確認は、行われません。
XPath 注入. コード注入 は、権限やデータへの不正アクセスを目的として悪意のあるコードがシステムに導入された場合に発生します。これは、ユーザーが入力したデータの型や内容に関する開発者の仮定を悪用することに基づいています。これについては、XPath も例外ではありません。
この問題が発生する可能性のある一般的なシナリオは、アプリケーション内で次のような XPath 式を使ってログイン名とパスワードの組み合わせを XML ファイル内の対応する情報とマッチングさせて承認を処理するような場合です。
//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id
この XPath 式は次のような SQL ステートメントと同等です。
SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';
XPath を採用した PHP アプリケーションでは、次のようにログインプロセスが処理される可能性があります。
<?php $file = "users.xml"; $login = $POST["login"]; $password = $POST["password"]; $xpath = "//user[login/text()=$login and password/text()=$password]/attribute::id"; if( file_exists($file) ) { $xml = simplexml_load_file($file); if($result = $xml->xpath($xpath)) echo "You are now logged in as user $result[0]."; else echo "Invalid login name or password."; } else exit("Failed to open $file."); ?>
入力の確認がまったく行われていません。これは、悪意のあるユーザーがログイン名とパスワードの両方に
' or 1=1
と入力してテストを「無効」にできることを意味します。その場合、$xpath
の評価結果は次のようになります。「」
//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id
角括弧内の式は常に
true
と評価されるので、これは結局次の式と同じになります。これは、XML
ドキュメント内のすべての
user
要素の
id
属性にマッチします。
//user/attribute::id
この特定の攻撃を回避する方法の 1
つは、$xpath
の定義内に挿入される変数名を単に引用符で囲み、Web
フォームから渡された値が強制的に文字列に変換されるようにすることです。
$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";
これは、SQL 注入攻撃を回避する際にしばしば勧められるのと同じ手法です。一般に、XPath 注入攻撃を回避するために従うべき手法は次のように、SQL 注入を回避するための手法と同じになります。
アプリケーション内で、テストされていないユーザーデータを決して受け入れないようにします。
すべてのユーザー入力データの型を確認し、不正な型を持つデータは拒否するか変換します
数値データの値が範囲外になっていないかテストし、範囲外の値については切り詰め、丸め、または拒否を行います。文字列に不正なキャラクタが含まれていないかテストし、不正なキャラクタが含まれる入力は削除するか拒否します。
明示的なエラーメッセージはシステムのセキュリティーを突破するのに役立つ手掛かりを不正ユーザーに与える可能性があるため、そのようなメッセージを出力しないようにします。これらの情報は代わりにファイル内やデータベースのテーブル内にロギングしてください。
SQL 注入攻撃によってデータベーススキーマに関する情報を入手できるのとまったく同じく、XPath 注入によって XML ファイル内をくまなく調べ、その構造を解明することができます。詳細については、Amit Klein 氏の論文「Blind XPath Injection」 (PDF ファイル、46K バイト) を参照してください。
クライアントに送り返される出力を確認することも重要です。MySQL
の ExtractValue()
関数を使用すると何が起こるのかを検討してみましょう。
mysql>SELECT ExtractValue(
->LOAD_FILE('users.xml'),
->'//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
->) AS id;
+-------------------------------+ | id | +-------------------------------+ | 00327 13579 02403 42354 28570 | +-------------------------------+ 1 row in set (0.01 sec)
ExtractValue()
は複数のマッチを空白で区切られた単一の文字列として返すため、この注入攻撃により、users.xml
に含まれるすべての有効な ID
が、単一の出力行としてユーザーに提供されます。追加の保護手段として、ユーザーに出力を返す前にその出力のテストも行うべきです。ここに単純な例を記します。
mysql>SELECT @id = ExtractValue(
->LOAD_FILE('users.xml'),
->'//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id'
->);
Query OK, 0 rows affected (0.00 sec) mysql>SELECT IF(
->INSTR(@id, ' ') = 0,
->@id,
->'Unable to retrieve user ID')
->AS singleID;
+----------------------------+ | singleID | +----------------------------+ | Unable to retrieve user ID | +----------------------------+ 1 row in set (0.00 sec)
一般に、ユーザーにデータを安全に返すためのガイドラインは、ユーザー入力を受け入れるためのガイドラインと同じです。それらは次のようにまとめることができます。
送信データの型のテストやその値が許容範囲に収まっているかのテストを常に行います。
エラーメッセージはアプリケーションの悪用に役立つアプリケーション関連情報を提供する可能性があるため、そのようなメッセージを不正ユーザーが決して表示できないようにします。