脆弱性コラム - 第6回
サイト脆弱性をチェックしよう! -- 第6回:SQLインジェクションの検査方法
前回は、クロスサイトスクリプティング(XSS)について説明したが、今回はXSSと並び、話題となっているSQLインジェクションについて説明する。2年前に立て続けにSQLインジェクションの脆弱性を攻撃された事件が発生したため、この言葉を聞いたことがある人は多いことだろう。それ以前は、SQLインジェクションの脆弱性を攻撃するには、SQLやプログラミングの知識が必要とされていた。
しかし、ちょうど3年ほど前に、SQLインジェクションの脆弱性を攻撃するためのツールが作成されたため、このツールさえ使えれば、誰でも簡単にアプリケーションを攻撃することができるようになった。その結果、2年前よりSQLインジェクションを利用した情報漏えいがたびたび発生することとなった。
SQLインジェクションとは、データベースを使用する場合に、任意のSQL文を実行させてしまう攻撃だ。このため、情報漏えいやデータの削除といった大きな被害をもたらしてしまう。
実際、独立行政法人 情報処理推進機構(IPA)の「ウェブアプリケーションに関する脆弱性届出件数」を見ても、XSSに次いで2番目に挙げられている。
SQLインジェクションの仕組み
この脆弱性の検査方法を説明する前に、SQLインジェクションの仕組みについて説明しよう。多くのアプリケーションでは、データをデータベースで管理し、SQL言語を用いて、管理しているデータを、検索、追加、更新、削除している。たとえば、検索を行うSQL文は以下のようになる。
この例では、userTableというテーブルから、userIDが「20070001」でかつ、 passwordが「passwd」であると言う条件を満たすデータを検索し、userIDとuserNameの値を取得する。一般にアプリケーションでは、任意の条件で検索を実施するため、「20070001」や「passwd」といった条件として、入力データを使用する。
このとき、入力データをそのまま検索条件として使用していると、任意のSQL文が実行できてしまう。先の例であれば、検索条件として、userIDに「1 or 1=1 or 1=1」やpasswordに「' or 'a'='a」といった検索条件を入れることで、以下のようなSQL文が作成され、すべてのデータを取得することが可能となってしまう。
このとき、入力データをそのまま検索条件として使用していると、任意のSQL文が実行できてしまう。先の例であれば、検索条件として、userIDに「1 or 1=1 or 1=1」やpasswordに「' or 'a'='a」といった検索条件を入れることで、以下のようなSQL文が作成され、すべてのデータを取得することが可能となってしまう。
このように、入力データをそのままSQL文の検索条件や、追加/更新データなどに使用していると、本来の意図とは違うSQL文が実行されてしまう。先の例では、本来数値項目であるuserIDの検索条件に数値以外の文字が含まれていたり、SQL文で文字列定数の区切りを示す「'」をエスケープ処理せずに使用しているために意図しないSQL文の実行が可能となる。
SQLインジェクションの検査手法
SQLインジェクションは、特別なHTTPリクエストを送信すると、HTTPレスポンスに、「SQLインジェクションが可能です」といった文字列が、常にXSSのように含まれるわけではない。このため、XSSの検査に比べて、脆弱性の有無が分かりにくいことが多い。しかし、以下のような方法で、SQLインジェクションの脆弱性の有無をある程度(単純なケースならば)判断することが可能だ。
- エラーメッセージの内容を元に確認
- 特別な値を入力データとする複数のHTTPリクエストに対するHTTPレスポンスの違いを元に確認
それでは、それぞれの方法で、どのように検査を実施するか見ていこう。
1. エラーメッセージの内容を元に確認
この方法は、組み立てられたSQL文が文法エラーとなるように入力データを細工したリクエストを送信し、そのHTTPレスポンスにシステムが出力する文法エラーが含まれているかどうかで判断する。この方法で検査する場合に使用する検査データには、たとえば以下のようなものがある。
1. エラーメッセージの内容を元に確認
この方法は、組み立てられたSQL文が文法エラーとなるように入力データを細工したリクエストを送信し、そのHTTPレスポンスにシステムが出力する文法エラーが含まれているかどうかで判断する。この方法で検査する場合に使用する検査データには、たとえば以下のようなものがある。
- 「;」を用いてSQL文を強制的に終了させるケース
- 「'」を用いて文字列定数を強制的に終了させるケース
これらの値をパラメータ値として使用した場合、先の例のSQL文では以下のようなSQL文が作成される。
これらは、SQL文として不適切であるため、データベースがエラーをアプリケーションに返す。サーバの設定に依存するが、アプリケーションは、データベースから受け取ったエラーをそのままHTTPレスポンスとして返すことがあり、検査ではデータベースのエラーがHTTPレスポンスに含まれていないかを確認する。
2. 特別な値を入力データとする複数のHTTPリクエストに対するHTTPレスポンスの違いを元に確認
実行時のエラー処理がなされているため、1.のような検査方法では脆弱性の有無が判断できないケースがある。この場合、入力データがそのままSQL文として組み立てられると、違う入力データであるにもかかわらず同じ結果が常に返ってくるなど、特定の処理が行われる検査データを含むHTTPリクエストを複数送信し、それらのHTTPレスポンスの違いからSQLインジェクションの有無を判断する。
これは、SQL文が文字列で表現されており、異なるSQL文であっても同じ結果を得ることができることを利用している。この方法で検査する場合に使用する検査データには、たとえば以下のようなものを元の入力データ(正常に処理が行える入力データ)に追加する。
2. 特別な値を入力データとする複数のHTTPリクエストに対するHTTPレスポンスの違いを元に確認
実行時のエラー処理がなされているため、1.のような検査方法では脆弱性の有無が判断できないケースがある。この場合、入力データがそのままSQL文として組み立てられると、違う入力データであるにもかかわらず同じ結果が常に返ってくるなど、特定の処理が行われる検査データを含むHTTPリクエストを複数送信し、それらのHTTPレスポンスの違いからSQLインジェクションの有無を判断する。
これは、SQL文が文字列で表現されており、異なるSQL文であっても同じ結果を得ることができることを利用している。この方法で検査する場合に使用する検査データには、たとえば以下のようなものを元の入力データ(正常に処理が行える入力データ)に追加する。
これらの値をパラメータ値として使用した場合、先の例のSQL文では以下のようなSQL文が作成される。
この例は、入力データが検索条件に使用される場合の検査だが、同じような考え方により、入力データを更新、追加する値として使用している場合についても適切なSQL文を作成する複数の入力データを用いることで、脆弱性の有無を検出することが可能だ。
対策
SQLインジェクション対策の基本はXSSと同じく適切なエスケープ処理を行うことだが、最近の開発環境の多くは、Prepared Statementあるいはバインドメカニズムと呼ばれる仕組みが実装されているので、それらを用いることが最も簡単な方法といえる。特殊なケース
前回もブラウザに依存して発生する脆弱性が存在するケースがあると記述したが、SQLインジェクションについても同様に、使用しているデータベースエンジンやミドルウェア、開発言語などに依存して発生する脆弱性がある。こうした例として、MySQLの脆弱性を利用したものがある。これはMySQLが日本語などのマルチバイトの文字列処理に問題があったため発生する脆弱性だ。
また、使用している文字コードが開発言語、データベースエンジンで異なっていると通常のSQLインジェクション対策では対応できないことがある(注1:次ページ参照)。その他にも、Microsoft SQL ServerにはOSコマンドを実行できるSQL文が用意されているため、データベースサーバを踏み台として利用するといったより大きな被害をもたらすことが可能となる場合がある(これらはバージョンアップや適切な設定により回避することが可能)。
SQLインジェクションについても使用している開発環境やミドルウェア、データベースエンジンの脆弱性を随時把握しておき、対策していく必要があるだろう。
注1:文字コードを利用した脆弱性
文字コードを利用した脆弱性はいくつか存在するが、代表的なものとして以下のようなものがある。
1. 文字コードの変換による脆弱性
各ソフトウェア(データベース、ミドルウェア、開発言語)で取り扱っている文字コードが異なる場合、文字コードを変換する際に、別の意味を持つ文字に変換されることがあり、結果的にSQLインジェクションなどの脆弱性につながることがある。
特に、Unicodeから他の文字コード(Shift-JISなど)に変換する場合、Unicodeにあって、他の文字コードにない文字を似たような文字に変換してしまうことがある。たとえば、Unicodeのバックスラッシュ(半角の\)と円記号(\)など。
Unicodeではこれらを別のもの(それぞれ0x5CとU+00A5)と規定しているが、他の文字コード(Shift-JIS、EUC、JIS)では、同じ0x5Cと規定している。
このため、ウェブアプリケーションではUnicodeを使用しており、データベースではShift-JISを使用している場合、文字コードの変換により、ウェブアプリケーションでは問題がないU+00A5が、データベースではエスケープ文字を表すことがある0x5C(\)に変換され、正しくエスケープされなくなる。
結果として、SQLインジェクションが発生する場合がある。たとえば、ウェブアプリケーションではUnicodeを使用し、データベースではShift-JISを使用している場合、以下のようなSQL文の“入力データ1”に「0xA5」、“入力データ2”に「 or 1=1;--」という値を使用すると、データベースでは以下のようなSQL文を意味する。
- 変換前
SELECT userID, userName FROM userTable WHERE
userName = '入力データ1' and address =
'入力データ2';
- 変換後
SELECT userID, userName FROM userTable WHERE
userName = '\' and address = ' or 1=1;--';
このデータベースで「\」をエスケープ文字として利用できる場合、userNameの検索条件の文字列定数の終了を意味する「'」が検索条件の一部と解釈され、すべてのデータを検索するSQL文を実行してしまう。
2. マルチバイト文字の解釈のミスによる脆弱性
正しく日本語に対応していないソフトウェアでは、文字の1バイト目だけを見てその文字がマルチバイトの文字か、1バイトの文字かを判断していることがあり、0x81などのデータを用いることで、文字列定数の区切り記号である「'」を無効化できることがある。
たとえば、データベースがこのようにマルチバイト文字を解釈しているとすると、先のSQL文の例であれば、入力データ1に0x81、入力データ2に「 or 1=1;--」を使用した場合、そのSQL文は以下のようになる。
SELECT userID, userName FROM userTable WHERE
userName = '・ and address = ' or 1=1;--';
この結果、userNameの検索条件の文字列定数の終了を意味する「'」が別の文字と解釈されてしまい、全件が検索結果として返されてしまう。
これら文字コードを利用した脆弱性は、SQLインジェクションに限ったことではない。他の脆弱性でも文字コードに依存した問題が発生することがあるので注意が必要だ。
2. マルチバイト文字の解釈のミスによる脆弱性
正しく日本語に対応していないソフトウェアでは、文字の1バイト目だけを見てその文字がマルチバイトの文字か、1バイトの文字かを判断していることがあり、0x81などのデータを用いることで、文字列定数の区切り記号である「'」を無効化できることがある。
たとえば、データベースがこのようにマルチバイト文字を解釈しているとすると、先のSQL文の例であれば、入力データ1に0x81、入力データ2に「 or 1=1;--」を使用した場合、そのSQL文は以下のようになる。
SELECT userID, userName FROM userTable WHERE
userName = '・ and address = ' or 1=1;--';
この結果、userNameの検索条件の文字列定数の終了を意味する「'」が別の文字と解釈されてしまい、全件が検索結果として返されてしまう。
これら文字コードを利用した脆弱性は、SQLインジェクションに限ったことではない。他の脆弱性でも文字コードに依存した問題が発生することがあるので注意が必要だ。
PICK UP
イベント・セミナー
AppScanに
関するお問い合わせ
テクマトリックス株式会社
東京本社ネットワークセキュリティ事業部
第3営業部
セキュリティプロダクツ営業2課03-4405-7814
- メールでのお問い合わせ
- watchfire@techmatrix.co.jp