今夜分かるSQLインジェクション対策Security&Trust ウォッチ(42)

» 2006年11月02日 10時00分 公開
[上野宣@IT]

【関連記事】

本内容についてのアップデート記事を公開しています。あわせてご確認ください(編集部)

Security&Trustウォッチ(60)
今夜こそわかる安全なSQLの呼び出し方 〜 高木浩光氏に聞いてみた

http://www.atmarkit.co.jp/fsecurity/column/ueno/60.html


 Webアプリケーションに対する攻撃手法の1つであるSQLインジェクションの存在は、かなり広く知られるようになった。しかし、その対策はまだ本当に理解されていないように思える。フォームから渡された値の特殊文字をエスケープしたり、PHPのmagic_quotes_gpcといった自動エスケープ機能をオンにするだけで対策したつもりになっていないだろうか。

 基本はもちろん、セカンドオーダーSQLインジェクションやマルチバイト文字を利用したSQLインジェクションの攻撃パターンや、その対策などを見直してみよう。

SQLインジェクションの基本

 SQLインジェクション(SQL Injection)はWebアプリケーションに対する攻撃手法の1つで、SQLを使って不正にデータベースを操作することを目的としている。SQLというのは、データベースを操作するために一般的に使われている言語である。

 このSQLインジェクションによる攻撃は、今日の脆弱性報告ブーム(?)の一端を担う手法であり、情報処理推進機構(IPA)に届け出られたWebアプリケーションの脆弱性関連情報(2006年9月まで)によると、第1位(41%)のクロスサイトスクリプティングに続いて、第2位(21%)がSQLインジェクションである。Webアプリケーションに携わっていて、この言葉を知らない人はかなり少なくなった(と思いたい)。

 SQLインジェクションの脆弱性を持ったアプリケーションのよくある例としては次のようなものである。

 これは、ユーザー名とパスワードを入力してログインする処理の一部である。uidとpwdの組み合わせが、データベースのものと一致すると認証するという仕組みだとする。

SELECT * FROM user WHERE uid='$uid' AND pwd='$pwd' 

 ユーザーからの入力は、ユーザー名が$uid、パスワードが$pwdに渡されるものとする。ここで、以下のような入力があった場合の挙動を考えてみよう。

 $uid   :  ueno

 $pwd  :  ' OR 'A'='A


 これを入力するとSQLは以下のようになる。

SELECT * FROM user WHERE uid='ueno' AND pwd='' OR 'A'='A'

 これが実行されるとORの後は常に真なので、パスワードが異なるにもかかわらず認証されてしまうという現象が起きてしまう。

 ここで取り上げた認証の回避以外にも、SELECTやDELETEといったSQLを用いた不正なデータベース操作が実行できてしまう可能性もある。テーブル名などが分からない場合でも、サブクエリーを利用して存在するテーブルが検索できる場合もある。

【関連リンク】隠されていたSQLインジェクション

http://www.atmarkit.co.jp/fsecurity/rensai/hoshino09/hoshino01.html


 この問題の対策としては、入力値には半角英数字のみを許可するよう制限する方法や、以下の例のようにSQLで使える特殊文字をエスケープして対処するという方法が一般的になっている。

 '    →  ''

 \   →  \\

【注】

「\」を拡張してるRDBMSでは「\\」とエスケープする必要がある


 この対策自体の方向性は正しいのだが、SQLインジェクションを理解していないため、対策が漏れている場合がある。

セカンドオーダーSQLインジェクション

 SQLインジェクション対策として、入力値を適切にエスケープするという対策を行うが、この「入力値」が何なのか間違えていたり、チェックが漏れている場合がしばしば見受けられる。また、HTMLを生成する段階でエスケープすればよいと考えている人も見受けられるが、それも間違いである。

 フォームに入力可能な部分だけが対策個所ではない。不正な入力の可能性を考える必要があるのは、GETやPOSTなどのクエリーやCookieの値、HTTPヘッダなど、HTTP経由で送られてくるもの“すべて”と、それに加えてデータベースやファイルなどに保存されたデータを呼び出す際にも対策を怠ってはならない。ここに問題があると、セカンドオーダーSQLインジェクションと呼ばれる脆弱性を持つことになる。

 次のような$uidの値を受け付けたときに「'」を「''」としてエスケープしてデータベースに保存したとする。

 $uid  :   ueno' union……


イメージ図

 文字列をエスケープして登録しても、次にデータベースから呼び出した際には、エスケープされていない元のデータが呼び出されることになる。この文字列をSQLに使用してしまうと、そこでSQLインジェクションが成立してしまう。

 入力した時点ですぐに効力を発揮せずに、次にアプリケーションで利用されたときに効力を発揮するこの攻撃をセカンドオーダーSQLインジェクションと呼ぶ。

シングルクオーテーションを使わないSQLインジェクション

 SQLインジェクション対策というと、「'」(シングルクオーテーション)の扱いばかりが注目されるが、「'」を使わなくても成立する攻撃もある。

 以下の例を見てみよう。

SELECT name FROM user where uid = '$uid' AND age > $age

 このSQLに渡される$uidと$ageの特殊文字は、適切にエスケープされているとしよう。ここに次のような値を渡す。

 $uid   :  ueno

 $age  :  31 UNION……


 これを入力するとSQLは以下のようになる。

SELECT name FROM user where uid = 'ueno' AND age > 31 UNION……

 これを実行するとUNION以降のSQLも実行されてしまうことになる。この問題は、ageの値がシングルクオーテーションで囲われていなかったことが原因である。以下のように囲われていれば、この問題は起きることがない(もちろん、数値以外は受け付けないという前段階の処理も欲しい)。

SELECT name FROM user where uid = '$uid' AND age > '$age'

マルチバイト文字の問題

 マルチバイト文字というのは、シフトJISやEUC-JPなどの2バイト文字などのことを指す。このマルチバイト文字を適切に処理しないと思わぬところでSQLインジェクションが発生することがある。

 PHPならばaddslashes()といった関数を使って、フォームから受け取った入力値に含まれる「'」を「\'」と置換するなどの処理をしてSQLインジェクションによる攻撃を回避しようとするかもしれない。

 以下の例を見てみよう。

 \x97' OR A=A   →   予' OR A=A


 「\x97' OR A=A」を前述のように「'」を「\'」とエスケープして処理してみると、「予' OR A=A」と変換されることがある。これはシフトJISの環境で発生する現象だ。

 この現象を詳しく見てみよう。

 \x97' → \x97\'


 ポイントとなるのは「\x97'」の部分で、これの「'」をエスケープするために「\'」と置換すると「\x97\'」となる。

 「\'」の部分もエンコードしてみると、「\x5C\x27」となり、全体では「\x97\x5C\x27」となる。

 \x97\x5C\x27 → '


 これを文字に戻すと、手前から「\x97\x5C」が2バイト文字として扱われて「予」となり、「\x27」が1バイト文字として扱われてしまい「'」となる。

 予' OR A=A


 結果として、「'」を「\'」とエスケープしたはずなのに、「'」が残ってしまうという現象が起きる。

 シフトJISは2バイト文字だが、中には2バイト目に1バイト文字の文字コードを含んだ文字がある。そのためにこのような現象が起きる。

 \x94\x5C → 能

 \x95\x5C → 表

 \x96\x5C → 暴


 「\x97'」の組み合わせ以外にも多数のパターンが考えられる。「'」を「''」とエスケープしている場合にも同様の現象が起きる可能性がある。

 これを回避するためには、クライアント側の文字コードにシフトJISを使うのをやめてEUC-JPなどを使うという方法がよい。

 しかし、諸事情により文字コードを変更できないという場合もあるかもしれない。その場合には、半端な1バイト文字を受け付けない処理を書くか、以下のようにマルチバイトを扱う関数を通して、文字コードを整理するという手もある。

  • Perlの場合 :Encode::from_to($str, "sjis","sjis");
  • PHPの場合 :mb_convert_encoding($str, 'SJIS', 'SJIS')

 PHPなどで用意されているmagic_quotes_gpcといった自動エスケープの機能が有効になっている場合には特に注意が必要である。思いがけないところで、このような現象が発生する余地を残している可能性がある。できるだけ全体に有効な自動エスケープの機能は無効にして、必要な個所を適切にエスケープ処理することが重要である。

 このマルチバイトの問題はSQLインジェクション以外に、クロスサイトスクリプティングなどの攻撃にも利用できるので対策の際には見落としがないように注意していただきたい。

【関連リンク】マルチバイトの落とし穴マルチバイトの落とし穴 http://www.atmarkit.co.jp/fsecurity/rensai/hoshino10/hoshino01.html


SQLインジェクション対策

 SQLインジェクションがはやり始めた2005年ごろに比べると、その手口は凝ったものになりつつある。今後はさらに巧妙な手口が現れてくる可能性もある。SQLインジェクション対策のポイントを見直してみよう。

●Webアプリケーションの対策

  • SQLを埋め込むところで特殊文字を適切にエスケープ
  • シフトJISの場合には1バイト文字を整理
  • SQLの記述をなくすためにO/R(Object/Relational)マッピングを活用
  • 攻撃者に役立つ情報を与えないために、不要なエラーメッセージ(データベースが出力するエラーなど)の表示を抑止
  • バインドメカニズムの利用

Webアプリケーション周りの対策

  • WAF(Web Application Firewall)による不正な文字の検出・防御(もしくは、ホワイトリストによる通過の許可)
  • IDS(Intrusion Detection System)、IPS(Intrusion Prevention System)などによる不正な文字の検出・防御

データベースの対策

  • アカウント分離と権限の最小化による適切なアクセス制御
  • 管理者アカウントなどの越権から守るために必要個所を適切に暗号化
  • 万が一の侵入の際に検証可能なようにログを適切に取得

 いまさらソースコードに手を入れることはできないが、システムは生かさなければならないという状況では、WAFの導入は効果があるはずである。

 また、どれも1つだけで決定的な手段というわけではないので、対策の際には併用してセキュリティレベルを高めることをお勧めする。


 今回はSQLインジェクションに特化して説明しているので、対策もSQLインジェクション中心に行ってきた。もちろん、クロスサイトスクリプティングなどほかのセキュリティ対策も併せて実施していただきたい。

Profile

上野 宣(うえの せん)

株式会社トライコーダ代表取締役

ネットワーク・セキュリティ監査、セキュリティ対策・運用改善コンサルティングを主な業務としている。

情報セキュリティを世に広げるべく、講演や執筆活動とさまざまな方面で活動中。近著に「今夜わかるメールプロトコル」、「今夜わかるTCP/IP」、「今夜わかるHTTP」(共に翔泳社)がある。個人ブログは「うさぎ文学日記


●修正履歴

【2006/11/2】

本文中のSQLで使える特殊文字をエスケープ例について、注記として「「\」を拡張してるRDBMSでは「\\」とエスケープする必要がある」を追記いたしました。

また、セカンドオーダーSQLインジェクションの項におけるエスケープ例を「\'」から「''」へ修正いたしました。

【2006/11/6】

SQLインジェクション対策、Webアプリケーションの対策について、「入力値のSQLの特殊文字を適切にエスケープ 入力値=プログラム(プロセス)に外部から入ってくるもの 」を「SQLを埋め込むところで特殊文字を適切にエスケープ」に修正し、「バインドメカニズムの利用」を追記いたしました。


「SecurityTrust ウォッチ」バックナンバー

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。