XMLマスターポイントレッスン ~ プロフェッショナル(データベース)編 ~
第9回 XQueryインジェクションの種類と対応策
インフォテリア株式会社 野中康弘 NONAKA, Yasuhiro
注意!
本記事にはSQLインジェクション、XQueryインジェクションに関する情報が含まれています。これらを 他者が運営するWebサイトに向けて仕掛けると、最悪の場合刑事罰および損害賠償請求の対象となります。 インジェクションの調査/検証は、必ずご自身の管理下のコンピュータおよびローカルエリアネットワーク で行なってください。本記事を参考にした行為により問題が生じても、筆者、筆者が所属する会社および 翔泳社は一切責任を負いません。
今回はXMLDBを利用したユーザーの入力を伴うWebアプリケーションなどから不正に情報を取得/改ざんする手法である「XQueryインジェクション」について、攻撃手法と一般的な対策方法を具体例とともに解説します。安易に考えていると、実際に問題が起こったときに深刻な被害となる可能性がありますので、XMLDBの運用/管理に携わる際の必須知識と言えます。この機会にしっかりと理解しておきましょう。
インジェクションとは
「インジェクション」と言うと、大半の方は「SQLインジェクション」という言葉を耳にしたことがあるかと思います。SQLインジェクションは、RDBを利用したユーザーの入力を伴うWebアプリケーションなどから不正に情報を得たり、改ざんしたりする手法です。例えば図1のようなログイン処理があるとします。
LIST1:実行されるSQL文
SELECT * FROM user_info
WHERE id = ユーザーID欄の入力値
AND password = パスワード欄の入力値
入力されたユーザーIDとパスワードを条件にuser_infoテーブル(表1)を検索し、条件に合致したデータを取得します(ここでは入力値のチェックを行なっていないものとします)。表1:user_infoテーブル
ID | PASSWORD | NAME | NICKNAME | ADDRESS |
yamada@abcde.co.jp | taro1234 | 山田太郎 | 太郎 | 東京都品川区・・・・・・ |
j_taka@fghijk.com | j_taka7777 | 高橋次郎 | 次郎 | 東京都渋谷区・・・・・・ |
hana_suzi@lmno.com | hnkszk5678 | 鈴木花子 | 花子 | 東京都新宿区・・・・・・ |
LIST2:SQLインジェクションの例(1)
SELECT * FROM user_info
WHERE id = 'abcdefg'
AND password = " or 'X'='X'
また、LIST3のようにユーザーID欄に「' or 'X'='X' --」と入力すると、SELECT文のWHERE句が『id = '' もしくは 'X' = 'X'』となります。LIST3:SQLインジェクションの例(2)
SELECT * FROM user_info
WHERE id = " or 'X'='X' --'
AND password = 'abcdefg'
さらに『--』を記述することで『--』以降の条件文をコメント(無効)にできます。その結果、常に『'X' = 'X'』が一致しますので、対象テーブルの全件の情報を取得できるというワケです(図3)。これ以外にも、さまざまな方法を使ってRDBから不正にデータを取得したり、データを改ざんしたりできます。これはXMLDBで使用されるXQueryにおいても同じことが言えます。XQueryの場合は「XQeuryインジェクション」と呼びます。SQLインジェクション、XQueryインジェクションのほかにも、インジェクション攻撃には「OSコマンドインジェクション」や「LDAPインジェクション」などがあります。
XQueryインジェクションによる攻撃手法
XQueryインジェクションによって、XMLDBで管理されているデータを不正に取得されたり、データを改ざんされたり、データを削除されたりするなどの危険性があります。まずはどのような攻撃手法があるのか、LIST4のUserInfoXML文書を使って、主なXQueryインジェクションを見ていきましょう。
LIST4:UserInfoXML文書
<UserInfo>
<User>
<ID>yamada@abcde.co.jp</ID>
<Password>taro1234</Password>
<Name>山田太郎</Name>
<NickName>太郎</NickName>
<Address>東京都品川区・・・</Address>
</User>
<User>
<ID>j_taka@fghijk.com</ID>
<Password>j_taka7777</Password>
<Name>高橋次郎</Name>
<NickName>次郎</NickName>
<Address>東京都渋谷区・・・</Address>
</User>
<User>
<ID>hana_suzu@lmno.com</ID>
<Password>hnkszk5678</Password>
<Name>鈴木花子</Name>
<NickName>花子</NickName>
<Address>東京都新宿区・・・</Address>
</User>
</UserInfo>
「or」を利用したインジェクション
前述のSQLインジェクションと同様に、XQueryインジェクションにもorを利用した攻撃が考えられます。Webアプリケーション内でLIST5のようなXQueryが記述されており、ユーザーから入力されたユーザーIDとパスワードを使ってXMLDBに問い合わせを実行するとします(図4)。
LIST5:実行されるXQuery
<UserList>{
let $user :=
fn:doc("UserInfo.xml")/UserInfo/User[ID/text()='ユーザーID欄の入力値' and ⇒
Password/text()='パスワード欄の入力値']
return
$user
}</UserList>
( 掲載の都合上、⇒ で折り返しています。 )
この場合も、ユーザーIDとパスワードが一致すれば、XMLDBより該当したデータを取得できます(図5)。ここで、ユーザーID欄、パスワード欄に「' or 'X' = 'X」と入力する(LIST6)とどうなるでしょうか。
LIST6:XQueryインジェクションの例(1)
<UserList>{
let $user :=
fn:doc("UserInfo.xml")/UserInfo/User[ID/text()=" or 'x'='x' and ⇒
Password/text()=" or 'x'='x']
return
$user
}</UserList>
( 掲載の都合上、⇒ で折り返しています。 )
XPath式の述部が『ID/text()='' or 'X'='X' and Password/text()='' or 'X'='X'』となり、常にtrueとなりますのでLIST4のUserInfoXML文書内のUser要素とその子要素のすべてを取得できます。また「' or '' eq '」と入力する(LIST7)ことで、XPath式の述部が『ID/text()='' or '' eq '' and Password/text()='' or '' eq ''』となり、こちらも常にtrueとなりますので、LIST4のUserInfoXML文書内のUser要素とその子要素のすべてを取得できます(図6)。LIST7:XQueryインジェクションの例(2)
<UserList>{
let $user :=
fn:doc("UserInfo.xml")/UserInfo/User[ID/text()=" or '' eq " and ⇒
Password/text()=' or '' eq']
return
$user
}</UserList>
( 掲載の都合上、⇒ で折り返しています。 )
このように、システムの実装によって異なりますが、この方法を用いることで不正にシステムにログインでき、データを取得、改ざん、削除される危険性が考えられます。
「,」を利用して別の式を実行するインジェクション
XQueryでは、各命令を「,」(カンマ)で区切ることで、それらを別の式として解釈し実行できます。例えば、return句において直接コンストラクタを記述するような場合もその1つです(図7)。
LIST8:実行される式(1)
<UserList>{
fn:string(
fn:doc("UserInfo.xml")/UserInfo/User[ID/text()='ユーザーID欄の入力値' and ⇒
Password/text()='パスワード欄の入力値']/NickName)
}</UserList>
( 掲載の都合上、⇒ で折り返しています。 )
ここでユーザーID欄に『']),//User(:』を、パスワード欄に『:),(<a/>['』を入力するとLIST9のような式になります。LIST9:実行される式(2)
<UserList>{
fn:string(
fn:doc("UserInfo.xml")/UserInfo/User[ID/text()="]),//User ⇒
(:' and Password/text()= :),(<a/>["]/NickName)
}</UserList>
( 掲載の都合上、⇒ で折り返しています。 )
ユーザーID欄に入力された『'])』の部分でXPath式の述部を『[ID/text()='']』とすることで、fn:string関数の返却値を空シーケンスにします。次に『,//User』の部分で『,』を使って『//User』を別の式と解釈させ、『(: 』からパスワード欄に入力された『 :)』までをコメントにします。さらに『,(<a/>['』の部分で、ここでも『,』を使って、『(<a/>['']/NickName)』の部分を別の式と解釈させます。この式を実行すると『//User』の部分だけの結果を表示することになり、該当のXML文書のUser要素とその子要素のすべてを取得できます(図8)。
また、この方法を用いることで、ある程度XML文書の構造を抽出することも可能になります。『//User』の部分は推測で入力されますが、推測できるパターンや文字の組み合わせなどを自動的に作成するようなスクリプトを用いることで、XMLDBに格納されているXML文書を抽出される危険性も考えることができます。
ここまで、2つのインジェクションについて理解できたところで、次はインジェクションに対して一般的にどのような対策を行なえば良いのかを解説していきます。
一般的なインジェクションの対処法
入力できる文字列に制限をかける
図1や図4などのユーザーの入力を伴うシステムにおいて、データベースへの問い合わせの条件となるような入力項目については、入力できる文字をアルファベットと数字のみに制限することで、ある程度の対策を行なうことができます。
例えば、図1や図4のログイン画面の入力欄をアルファベットと数字のみに制限します。これにより、入力欄には「' or 'X'='X'」や「' or '' eq ''」、「']),//User (: 」や「:),(<a/>['」などを入力できなくなります。しかし、ここではメールアドレスをユーザーIDとして使っていますので、入力制限によって「@」や「.」を含むメールアドレスそのものも入力できなくなってしまいます。そこで、メールアドレスの「@」以前と「@」以降、最初の「.」までを入力させ、「co.jp」などのドメインの一部をドロップダウンリストより選択させるなど、メールアドレスを部分的に入力させればアルファベットと数字しか入力できなくても問題なく利用できます(画面)。
上記の2つの対策のうちいずれかの対策を施し、さらに入力できる文字数にも制限をかけ、プログラムでそれらをチェックすることでインジェクションを防止できます。
特殊記号のエスケープ
ユーザーの入力項目において、前述の入力制限を設定していない場合は、半角スペースや「"」や「'」、「{}」や「()」などの特殊記号をエスケープすることによってインジェクションを防止できます。ユーザーに入力された文字列に対して半角スペースや「"」や「'」、「{}」や「()」などの特殊記号が入力されているかどうかをチェックし、入力されている場合はエスケープするといった処理を追加します。
さらに、入力された文字列が要素値や属性値となるような場合、上記の特殊記号に加え、XMLではおなじみの「<」や「&」はどうなると思いますか。皆さんはお分かりになっていると思いますが、これらについてももちろんエスケープ処理を行なう必要があります。
XMLでは「<」はタグの開始、「&」は参照の開始を表わす特別な記号ですので、「<」は「<」に、「&」は「&」に置き換える処理を行なわなくてはなりません。エスケープ処理では、定義済み実体参照があるものは定義済み実体参照に、定義済み実体参照がないものは文字コード(ISO/IEC10646)に置き換えるなどしてエスケープします。特殊記号とエスケープの対応を表2にまとめましたので参照してください。
表2:特殊文字とエスケープ
記号の種類 | エスケープ |
" | " |
' | ' |
< | < |
> | > |
& | & |
( | ( |
) | ) |
, | , |
{ | { |
} | } |
今月の確認問題
ここまで、XQueryインジェクションとその対策方法を解説してきました。今回解説した内容について、しっかりと理解できているかどうかを確認問題でチェックしてみましょう。
問題1
次の[ユーザー情報XML文書]に対して、下記の[XQuery]を使ってユーザー情報を取得します。[XQuery]の(1)、(2)に「' or 'a'='a」が入力された場合の結果として、正しいものを選択してください。
ただし、入力値のチェック、エスケープ処理は行なっていないものとします。
ユーザー情報XML文書(ex01.xml)
<UserInfo>
<User>
<Login>
<Id>yamada</Id>
<Pw>taro1234</Pw>
</Login>
<Name>山田太郎</Name>
<eMail>yamada@abcde.co.jp</eMail>
<NickName>太郎</NickName>
<Address>東京都品川区・・・</Address>
</User>
<User>
<Login>
<Id>hana_suzu</Id>
<Pw>hnkszk5678</Pw>
</Login>
<Name>鈴木花子</Name>
<NickName>花子</NickName>
<Address>東京都新宿区・・・</Address>
</User>
</UserInfo>
[ XQuery ]
<List>{
let $login :=
fn:doc("ex01.xml")//Login[Id/text()='(1)' and Pw/text()='(2)'],
$user := $login/..
return
$user
}</List>
- (1)、(2)に入力された値はエラーとなり、データを取得できない
<List>
<User>
<Login>
<Id>yamada</Id>
<Pw>taro1234</Pw>
</Login>
<Name>山田太郎</Name>
<eMail>yamada@abcde.co.jp</eMail>
<NickName>太郎</NickName>
<Address>東京都品川区・・・</Address>
</User>
<User>
<Login>
<Id>hana_suzu</Id>
<Pw>hnkszk5678</Pw>
</Login>
<Name>鈴木花子</Name>
<NickName>花子</NickName>
<Address>東京都新宿区・・・</Address>
</User>
</List>
<List>
<User>
<Login>
<Id>yamada</Id>
<Pw>taro1234</Pw>
</Login>
<Name>山田太郎</Name>
<eMail>yamada@abcde.co.jp</eMail>
<NickName>太郎</NickName>
<Address>東京都品川区・・・</Address>
</User>
</List>
<List>
<Login>
<Id>yamada</Id>
<Pw>taro1234</Pw>
</Login>
<Login>
<Id>hana_suzu</Id>
<Pw>hnkszk5678</Pw>
</Login>
</List>
解説
このXQueryは、入力されたID、パスワードと一致したUser要素とその子要素を取得するためのものです。XQueryの①、②の部分に「' or 'a'='a」が入力されることで、次のようなクエリになります。
<List>{
let $login := fn:doc("ex01.xml")//Login[Id/text()='' or 'a'='a' and ⇒
Pw/text()='' or 'a'='a'],
$user := $login/..
return
$user
}</List>
( 掲載の都合上、⇒ で折り返しています。 )
上記のXQueryを実行することで、常に「'a'='a'」がtrueとなるため、ex01.xml内にあるUser要素とその子要素すべてを取得できます。AはUser要素とその子要素すべてを取得しています。Bは[Id/text()='yamada' and Pw/text()='taro1234']と入力されたときの結果となりますので誤りです。Cはex01.xmlのLogin要素とその子要素ですので誤りです。Dは入力値のチェック、エスケープ処理を行なっていないことが前提となっており、「' or 'a'='a」が入力されてもエラーとはならないので誤りです。よって正解はAです。問題2
XQueryインジェクションへの一般的な対策として用いられるものを選択してください。
- アルファベットや数字のみなど、入力できる文字に制限を加える
- 入力された文字に対して、入力値のチェックを行なう
- データベースにインジェクション対策機能がある場合は利用する
- XQueryに対するインジェクション攻撃は少ないので対策は必要ない
解説
XQueryインジェクションへの一般的な対策法として、次のようなことが挙げられます。
- 入力できる文字に制限を加える
- 入力された文字に対して、入力値のチェックを行なう
- データベースにインジェクション対策機能がある場合はその機能を利用する
これを基に見ていくと、ACは正しい記述であることが分かります。しかし、DのようにXQueryに対してインジェクション攻撃が少ないからといって対策を怠っていると、取り返しのつかないことにもなりかねません。インジェクション攻撃の危険性がある場合には、インジェクションへの対策を行なう必要がありますので誤りです。よって、正解はA、B、Cです。
* * *
今回は主なXQueryインジェクション攻撃とその一般的な対応策について解説しましたが、これらはきちんと理解しておきましょう。ほとんどがアプリケーション側での対応となりますが、今回解説した内容はデータベースを管理する上で必須となる知識で、XQueryインジェクションの脅威と対応策をアプリケーション開発者に教示することもデータベース管理者として重要な役割となります。
また、今回解説した内容以外にもXQueryインジェクションとなりうるものも存在しますので、XQueryインジェクションについて詳しくなるためには、XQueryをしっかりと理解しなければなりません。
最後になりましたが、本記事で紹介するSQLやXQueryは解説を分かりやすくするため、すべての項目を取得/返却するものとなっています。実際のWebアプリケーションでは、このようなSQLやXQueryなどを書くことはありませんので、参考程度に見ていただければと思います。
次回はXMLデータの構造変更について解説します。
野中康弘(のなかやすひろ)
2004年よりインフォテリア株式会社 教育部に勤務。主にインフォテリア認定トレーニングセンターで開催されているXML教育のテキスト開発/改訂に従事。最近の気温の変化についていけず、風邪を引いてしまいました。皆さん、くれぐれも体調管理には気をつけましょう!
<掲載> P.212-218 DB Magazine 2008 January