XMLマスターポイントレッスン ~ プロフェッショナル(データベース)編 ~
第9回 XQueryインジェクションの種類と対応策

インフォテリア株式会社 野中康弘 NONAKA, Yasuhiro

注意!

本記事にはSQLインジェクション、XQueryインジェクションに関する情報が含まれています。これらを
他者が運営するWebサイトに向けて仕掛けると、最悪の場合刑事罰および損害賠償請求の対象となります。
インジェクションの調査/検証は、必ずご自身の管理下のコンピュータおよびローカルエリアネットワーク
で行なってください。本記事を参考にした行為により問題が生じても、筆者、筆者が所属する会社および
翔泳社は一切責任を負いません。

今回はXMLDBを利用したユーザーの入力を伴うWebアプリケーションなどから不正に情報を取得/改ざんする手法である「XQueryインジェクション」について、攻撃手法と一般的な対策方法を具体例とともに解説します。安易に考えていると、実際に問題が起こったときに深刻な被害となる可能性がありますので、XMLDBの運用/管理に携わる際の必須知識と言えます。この機会にしっかりと理解しておきましょう。

インジェクションとは

「インジェクション」と言うと、大半の方は「SQLインジェクション」という言葉を耳にしたことがあるかと思います。SQLインジェクションは、RDBを利用したユーザーの入力を伴うWebアプリケーションなどから不正に情報を得たり、改ざんしたりする手法です。例えば図1のようなログイン処理があるとします。

DBへのログイン処理の流れ1

ログイン画面でユーザーIDとパスワードを入力し、[Login]ボタンをクリックすることでアプリケーション内に記述されたSQL(LIST1)が実行されます。

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 鈴木花子 花子 東京都新宿区・・・・・・
このとき、悪意を持つ利用者がユーザーID、またはパスワードの入力エリアに、あるキーワードを入力することで不正に情報を引き出せるようになります。そのキーワードとは「or」です。本来であれば、ユーザーIDとパスワードが一致したデータを1件取得しますが(図2)、LIST2のようにユーザーID欄に任意の文字列(例ではabcdefg)を入力し、パスワード欄に「' or 'X'='X」と入力するとSELECT文のWHERE句が『id = 'abcdefg' かつ password = ''』もしくは『'X' = 'X'』となり、常に『'X' = 'X'』がtrueとなるので対象テーブルの全件の情報を取得できてしまうのです。

本来のSQLの検索結果

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)。

SQLインジェクションによる検索結果

これ以外にも、さまざまな方法を使って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>
     ( 掲載の都合上、⇒ で折り返しています。 )

DBへのログイン処理の流れ2

この場合も、ユーザーIDとパスワードが一致すれば、XMLDBより該当したデータを取得できます(図5)。

本来のXQueryの検索結果

ここで、ユーザー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インジェクションによる検索結果(1)

このように、システムの実装によって異なりますが、この方法を用いることで不正にシステムにログインでき、データを取得、改ざん、削除される危険性が考えられます。

「,」を利用して別の式を実行するインジェクション

 XQueryでは、各命令を「,」(カンマ)で区切ることで、それらを別の式として解釈し実行できます。例えば、return句において直接コンストラクタを記述するような場合もその1つです(図7)。

「 , 」で区切って別の式として実行させる例

これを悪用することで、orを利用したインジェクション同様、XMLDBの対象となるXML文書の全データを取得できます。具体例として、LIST8のような式があると仮定します。

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)。

XQueryインジェクションによる検索結果(2)

また、この方法を用いることで、ある程度XML文書の構造を抽出することも可能になります。『//User』の部分は推測で入力されますが、推測できるパターンや文字の組み合わせなどを自動的に作成するようなスクリプトを用いることで、XMLDBに格納されているXML文書を抽出される危険性も考えることができます。

ここまで、2つのインジェクションについて理解できたところで、次はインジェクションに対して一般的にどのような対策を行なえば良いのかを解説していきます。

一般的なインジェクションの対処法

入力できる文字列に制限をかける

図1や図4などのユーザーの入力を伴うシステムにおいて、データベースへの問い合わせの条件となるような入力項目については、入力できる文字をアルファベットと数字のみに制限することで、ある程度の対策を行なうことができます。
例えば、図1や図4のログイン画面の入力欄をアルファベットと数字のみに制限します。これにより、入力欄には「' or 'X'='X'」や「' or '' eq ''」、「']),//User (: 」や「:),(<a/>['」などを入力できなくなります。しかし、ここではメールアドレスをユーザーIDとして使っていますので、入力制限によって「@」や「.」を含むメールアドレスそのものも入力できなくなってしまいます。そこで、メールアドレスの「@」以前と「@」以降、最初の「.」までを入力させ、「co.jp」などのドメインの一部をドロップダウンリストより選択させるなど、メールアドレスを部分的に入力させればアルファベットと数字しか入力できなくても問題なく利用できます(画面)。

ログイン画面の入力制限

社内システムなどの利用範囲が限られたものについては、@以降のドメイン部分をプログラム内で付加しても良いでしょう。また、半角スペースや「"」や「'」、「{}」や「()」などの特殊記号を入力できない文字として指定することで、「' or 'X'='X'」や「' or '' eq ''」、「']),//User (: 」や「:),(<a/>['」などを入力できなくなります。
上記の2つの対策のうちいずれかの対策を施し、さらに入力できる文字数にも制限をかけ、プログラムでそれらをチェックすることでインジェクションを防止できます。

特殊記号のエスケープ

ユーザーの入力項目において、前述の入力制限を設定していない場合は、半角スペースや「"」や「'」、「{}」や「()」などの特殊記号をエスケープすることによってインジェクションを防止できます。ユーザーに入力された文字列に対して半角スペースや「"」や「'」、「{}」や「()」などの特殊記号が入力されているかどうかをチェックし、入力されている場合はエスケープするといった処理を追加します。
さらに、入力された文字列が要素値や属性値となるような場合、上記の特殊記号に加え、XMLではおなじみの「<」や「&」はどうなると思いますか。皆さんはお分かりになっていると思いますが、これらについてももちろんエスケープ処理を行なう必要があります。
XMLでは「<」はタグの開始、「&」は参照の開始を表わす特別な記号ですので、「<」は「&lt;」に、「&」は「&amp;」に置き換える処理を行なわなくてはなりません。エスケープ処理では、定義済み実体参照があるものは定義済み実体参照に、定義済み実体参照がないものは文字コード(ISO/IEC10646)に置き換えるなどしてエスケープします。特殊記号とエスケープの対応を表2にまとめましたので参照してください。

表2:特殊文字とエスケープ

記号の種類 エスケープ
" &quot;
' &apos;
< &lt;
> &gt;
& &amp;
( &#x0028
) &#x0029
, &#x002c
{ &#x007b
} &#x007d

今月の確認問題

ここまで、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. <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>
  2. <List>
     <User>
      <Login>
       <Id>yamada</Id>
       <Pw>taro1234</Pw>
      </Login>
      <Name>山田太郎</Name>
      <eMail>yamada@abcde.co.jp</eMail>
      <NickName>太郎</NickName>
      <Address>東京都品川区・・・</Address>
     </User>
    </List>
  3. <List>
     <Login>
      <Id>yamada</Id>
      <Pw>taro1234</Pw>
     </Login>
     <Login>
      <Id>hana_suzu</Id>
      <Pw>hnkszk5678</Pw>
     </Login>
    </List>
  4. (1)、(2)に入力された値はエラーとなり、データを取得できない

解説

この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インジェクションへの一般的な対策として用いられるものを選択してください。

  1. アルファベットや数字のみなど、入力できる文字に制限を加える
  2. 入力された文字に対して、入力値のチェックを行なう
  3. データベースにインジェクション対策機能がある場合は利用する
  4. 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

ページトップへ▲

XMLマスターポイントレッスン ~ プロフェッショナル(データベース)編 ~ 記事一覧へ戻る

HOMEへ戻る