レベル: 中級 Robi Sen (rsen@department13.com), Freelance Writer, Department13
2007年 7月 17日 単純な XML API、Web サービス、そしてリッチ・インターネット・アプリケーション (RIA) が普及したことにより、構成ファイルからリモート・プロシージャー・コールに至るすべてのものに対して XML をデータ・フォーマットとして採用する組織が増えてきました。なかには、フラット・ファイルやリレーショナル・データベースの代わりに XML 文書を使っている人までいます。しかし、外部のユーザーに対してデータの実行依頼を許可する他のあらゆるアプリケーションや技術と同じく、XML アプリケーションはコード・インジェクションによる攻撃、具体的には XPath インジェクション攻撃を受けやすいものです。
はじめに
新しく登場した技術が定着するようになると、その技術に対する脅威も同じく根強くなってきます。コード・インジェクションによる攻撃としては、ブラインド SQL インジェクション攻撃がよく知られていますが、その他にもさまざまな攻撃形態があり、一部には十分に立証あるいは理解されていないものもあります。新たに浮上してきたコード・インジェクションによる攻撃は、XPath インジェクション攻撃です。この攻撃は XPath パーサーの型付けの弱さと寛大な性質を利用し、悪意のある人が URL、フォーム、あるいはその他の方法に、悪意の XPath クエリーを結合させ、特権がなければアクセスできない情報にアクセスしてその情報を変更するというものです。
この記事では、XPath 攻撃が通常どのように実行されるかを検討し、Java™ および XML 環境での例を記載します。そしてこのような脅威を見つけ出す方法と脅威を和らげるするための対策を紹介し、最後に不正侵入が疑われるときの対処方法について説明します。
開始するにあたって
この記事で焦点とするのは、コード・インジェクションによる特定の攻撃タイプ、ブラインド XPath インジェクションです。XPath 1.0 に馴染みがないという方、あるいは入門書が必要という場合には、W3Schools の XPath チュートリアルを参照してください (リンクを「参考文献」に記載)。また、developerWorks には XPath の操作に関する豊富な記事が各国語で揃っています (リンクを「参考文献」に記載)。この記事では XPath 1.0 に焦点を絞った例を用いていますが、XPath 2.0 にも同じく適用できます。実際、XPath 2.0 では遭遇する可能性のある問題が多くなります。
この記事には Java JDK 5.0 で機能するように開発された Java コード・サンプルも記載しています。記事のコンセプトと話題はプラットフォーム間で共通していますが、アプリケーションで XPath を使って特定のコード・サンプルを表示する場合には、JDK 5.0 を使用する必要があります。
コード・インジェクション
Web アプリケーションに一般的な攻撃あるいは脅威の形態は、コード・インジェクションです。これをウィキペディアでは以下のように定義しています。
... システムが入力に関して条件を設定したり、チェックしないことを利用して、コンピューター・プログラムまたはシステムにコードを取り込む (または「注入する」) という攻撃手法のこと。一般的に、注入したコードが目的とするのは、本来意図されたプログラムの機能を迂回または変更することである。迂回された機能がシステム・セキュリティーの場合、その結果は破滅的なものとなる。
Web Application Security Consortium や Security Focus (それぞれのリンクを「参考文献」に記載) などの Web サイトをざっと読むだけでも、JavaScript から SQL インジェクション、そしてその他のコード・インジェクション攻撃に至るまで、ほとんどの攻撃が何らかの形態のコード・インジェクションであることが分かります。現在浮上してきている脅威は、Amit Klein が 2004年の記事で初めて概説したブラインド XPath インジェクション攻撃です (「参考文献」を参照)。この攻撃はブラインド SQL インジェクション攻撃とほとんど同じように作用しますが、SQL インジェクション攻撃とは違い、XPath 攻撃について知っている人、あるいは予防策を講じている人はほとんどいません。この脅威に対しては、SQL インジェクション攻撃と同じく、ベスト・プラクティスに従ってセキュア・アプリケーションを開発することで、大抵の場合は容易に対処することができます。
XPath 攻撃
一般的に、大抵の Web アプリケーションはリレーショナル・データベースを使用して情報を保存し、そこから情報を取得します。例えば Web サイトに認証が必要な場合、users というテーブルを作って、固有 ID、ログイン名、パスワード、そしておそらくロールなどのその他の情報を保存すると考えられます。この users テーブルのユーザーを検索する SQL クエリーは、リスト 1 のようになります。
リスト 1. SQL クエリーによる users テーブルのユーザー検索
Select * from users where loginID='foo' and password='bar'
|
このクエリーでは、ユーザーが loginID とパスワードを入力しなければなりません。アタッカーが loginID フィールドに ' or 1=1 と入力し、パスワードとして ' or 1=1 を入力した場合、このクエリーはリスト 2 のようになります。
リスト 2. アタッカーの入力によって作らされたクエリー
Select * from users where loginID = '' or 1=1 and password=' ' or 1=1
|
このクエリーの結果は常に一致となるため、アタッカーがシステムに侵入できるというわけです。XPath インジェクションはこれとほとんど同じように機能します。一方、users というテーブルではなく、XML ファイルにリスト 3 のようなユーザー情報が含まれる場合を考えてみてください。
リスト 3. user.xml
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user>
<firstname>Ben</firstname>
<lastname>Elmore</lastname>
<loginID>abc</loginID>
<password>test123</password>
</user>
<user>
<firstname>Shlomy</firstname>
<lastname>Gantz</lastname>
<loginID>xyz</loginID>
<password>123test</password>
</user>
<user>
<firstname>Jeghis</firstname>
<lastname>Katz</lastname>
<loginID>mrj</loginID>
<password>jk2468</password>
</user>
<user>
<firstname>Darien</firstname>
<lastname>Heap</lastname>
<loginID>drano</loginID>
<password>2mne8s</password>
</user>
</users>
|
XPath での場合、先ほどの SQL クエリーに対応したステートメントはリスト 4 のようになります。
リスト 4. SQL クエリーに対応する XPath ステートメント
//users/user[loginID/text()='abc' and password/text()='test123']
|
同じような攻撃で認証をバイパスするには、リスト 5 のようなコードが考えられます。
リスト 5. 認証のバイパス
//users/user[LoginID/text()='' or 1=1 and password/text()='' or 1=1]
|
Java アプリケーションでは doLogin のようなメソッドで、リスト 3 の XML 文書を使用して再度認証を実行することが考えられます。リスト 6 はその一例です。
リスト 6. XPathInjection.java
import java.io.IOException;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
public class XpathInjectionExample {
public boolean doLogin(String loginID, String password)
throws ParserConfigurationException, SAXException,IOException,
XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("users.xml");
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//users/user[loginID/text()='"+loginID+"'
and password/text()='"+password+"' ]/firstname/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
//print first names to the console
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());}
if (nodes.getLength() >= 1) {
return true;}
else
{return false;}
}
}
|
リスト 6 の場合、loginID に 'abc' などのログイン名を、そして password に 'test123' などのパスワードを入力すると、クラスが true を返すことになっています (サンプルの場合には、ファーストネームのリストもコンソールに出力されます)。そこで、例えば、' or 1=1 or ''=' といった値を入力すると、XPath はリスト 7 のようなストリングになるので、戻り値は常に true となるわけです。
リスト 7. ストリング
//users/user[loginID/text()='' or 1=1 or ''='' and password/text()='' or 1=1 or ''='']
|
論理的に言うと、上記は常に true を返し、常にアタッカーのアクセスを許可するクエリーとなります。
XPath でこれ以上に可能性が高く、おそらくなおさら厄介な攻撃は、アタッカーが XPath を利用してアプリケーション内で XML 文書をひそかに操作することです。
XML 文書構造の抽出
認証をバイパスするためのクエリーは、XML 文書に関する情報を抽出するためにも使用できます。例えば、アタッカーが XML 文書の最初のサブノードの名前が loginID であると推測し、確認を要求するとします。リスト 8 に、この場合のアタッカーの入力を示します。
リスト 8. アタッカーによる入力
abc' or name(//users/LoginID[1]) = 'LoginID' or 'a'='b
|
リスト 7 の 1=1 の代わりに、リスト 8 に記載した式によって最初のサブノードの名前が loginID であるかどうかをチェックします。この場合のクエリーはリスト 9 のとおりです。
リスト 9. クエリー
String(//users[LoginID/text()='abc' or name(//users/LoginID[1]) =
'LoginID' or 'a=b' and password/text()=''])
|
アタッカーは試行錯誤でこの XPath 式が認証に成功するかどうかを調べることによって、XML 文書のさまざまな子ノードをチェックし、情報を集めることができます。Klein の記事で述べられているように、アタッカーが単純なスクリプトを作成して、そのスクリプトでさまざまな XPath インジェクションを送信し、システムから XML 文書を抽出する恐れもあります。
XPath インジェクションの防止策
XPath インジェクション攻撃は SQL インジェクション攻撃とかなり似通っているため、SQL インジェクション攻撃を防ぐために使われている多くの防止策によって、この攻撃を阻止することができます。当然のことながら、これらの防止策のほとんどは、他の一般的なコード・インジェクション攻撃を防ぐためにも使用できます。
妥当性検査
アプリケーション、環境、そして言語が何であろうと、以下のベスト・プラクティスに従ってください。
- すべての入力には疑いを持ってかかること。
- データの型だけでなく、そのフォーマット、長さ、範囲、そして内容の妥当性も検査すること (例えば、
if (/^"*^';&<>()/) のような単純な正規表現にはとりわけ疑わしい特殊文字があるはずです)。
- クライアントとサーバーの両方でデータの妥当性検査を行うこと。クライアントの妥当性検査を回避するのは極めて簡単だからです。
- セキュアなソフトウェア開発のベスト・プラクティスに基づく、アプリケーション・セキュリティーのための一貫して伝えられているストラテジーに従うこと (Apache による Web サービスの場合の優れたベスト・プラクティスのリストについては、「参考文献」を参照してください)。
- アプリケーションをリリースする前に、既知の脅威に対してテストすること。テスト方法については、「参考文献」に記載した記事、「ファズ・テスト」を参照してください。
パラメーター化
大抵のデータベース・アプリケーションとは異なり、XPath はパラメーター化されたクエリーの概念をサポートしませんが、XQuery などの別の API を使用してこの概念を模倣することはできます。クエリーをパラメーター化するには、リスト 10 のように式をストリングとして構成してから XPath パーサーに渡して実行時に動的に実行するのではなく、リスト 11 のように、クエリーを保持する外部ファイルを作成するという方法を用います。
リスト 10. XPath パーサーに渡されるストリング
"//users/user[LoginID/text()=' " + loginID+ " ' and password/text()='
"+ password +" ']"
|
リスト 11 では、クエリーを保持する外部ファイルを作成してクエリーをパラメーター化しています。
リスト 11. dologin.xq
declare variable $loginID as xs:string external;
declare variable $password as xs:string external;//users/user[@loginID=
$loginID and @password=$password]
|
リスト 12 のように、リスト 11 に多少の変更を加えてクエリーをパラメーター化することも可能です。
リスト 12. XQuery スニペット
Document doc = new Builder().build("users.xml");
XQuery xquery = new XQueryFactory().createXQuery(new File("
dologin.xq"));
Map vars = new HashMap();
vars.put("loginid", "abc");
vars.put("password", "test123");
Nodes results = xquery.execute(doc, null, vars).toNodes();
for (int i=0; i < results.size(); i++) {
System.out.println(results.get(i).toXML());
}
|
上記では、重要な明示的変数 $loginID および $password が実行時に実行可能な式として処理されないようにしています。このように実行ロジックとデータを切り分けると、残念ながらクエリーのパラメーター化は XPath の一部になりませんが、SAXON (リンクを「参考文献」に記載) などのオープン・ソースのパーサーで自由に使用できるようになります。この種の機能を実現するパーサーは他にもあるため、この方法は XPath インジェクションに対する確実な防止策になるはずです。
Web サーバーでのデータ検査
XPath インジェクションとその他の形態のコード・インジェクションの両方を防ぐには、Web サーバーからバックエンド・サービスに渡されるすべてのデータをチェックする必要があります。例えば Apache では、SecFilterSelective THE_REQUEST "(\'|\")" などの Mod_Security フィルターによって、ストリングに含まれる単一引用符と二重引用符を検索して無効にすることができます。これと同じ手法を使って、さまざまなインジェクション攻撃に使用される可能性のある ("*^';&><</) などの特殊文字をフィルタリングし、無効にするのも一案です。この手法は、REST または SOAP ベースの XML サービスなどを使用する一部のアプリケーションには極めて効果的ですが、適用できないアプリケーションもあります。常に言えることですが、アプリケーションの初期設計から実装に至るまでのインテリジェントでセキュアな設計が最善の策となります。
もしもの場合
大多数の組織は脅威を検出してそれを拒絶することを考えてはいても、システムにセキュリティー・ブリーチが発生した場合の対処方法について、適切なセキュリティーの専門家を使って計画しているところはほとんどありません。常に最悪の事態を前提として対策を立てることが必要です。
どのような対策を立てるかは、組織、そして不正侵入されたシステムの種類によって大きく異なりますが、一般的に最善策となるのは、システムをオフラインにして専門のセキュリティー・エンジニアがシステムを点検するまで待つことです。システムをオフラインにしてすぐにドライブのイメージを更新する人もいますが、そうすると犯罪の証拠だけでなく、侵入者がシステムに対して行ったセキュリティー侵害に関する情報も失われてしまういます。可能な限り、システムの状態を保存してセキュリティーの専門家が調査できるようにしてください。
まとめ
XML を使用する大抵のアプリケーションは XPath インジェクション攻撃を受けやすいわけではないので、特定の脆弱性が見つかったからと言ってリスクが高いと考えるべきではありません。同時に、導入が進んでいる Ajax などの新しいプラットフォームや FLEX、Open Laszlo などの RIA プラットフォーム、そして Google のような複数の組織の XML サービスの統合では、バックエンド・サービスとの通信からパーシスタンスに至るまで、あらゆるものが XML の使用に大きく依存しています。そのような状況のなか、開発者はこれらの手法によって生じる脅威とリスクを認識していなければなりません。
特定の脅威は新しいとは言え、幸いなことに問題とそれを解決するための原則は新しいものではありません。セキュリティーのベスト・プラクティスに従うことが、XPath インジェクション攻撃だけでなく、他の形態の攻撃からも自らを守ることになります。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Robi Sen は IT コンサルタント会社、Department 13 LLC の副社長を務めています。彼は時間のほとんどを、フォーチュン 500 社から新興企業までのさまざまな顧客が技術的問題を定義し、管理する際の支援に費やしています。また、広範な技術に関する著作活動を行っている他、さまざまなコンベンションにも出席しています。 |
記事の評価
|