TOP設計・移行・活用> シングルクォートのエスケープ
脆弱なWebアプリケーション
脆弱なWebアプリケーション

第5回:インジェクション攻撃
著者:セントラル・コンピュータ・サービス  長谷川 武
2005/5/25
前のページ  1  2  3   4  次のページ
シングルクォートのエスケープ

   ご覧いただいたように、SQLインジェクション攻撃のポイントはシングルクォート「'」を使って文字列定数の中から脱出し、SQL文の構文に影響をおよぼすことである。したがって、これに対処するにはユーザが入力したシングルクォートを文字列の区切りとして機能させないことだ。これを「エスケープ処理」と呼ぶ。

   SQL文の文法では、文字列定数中でシングルクォート「'」を2個並べるとそれが1個のシングルクォートそのものを表すという約束になっている。たとえば次のような表記があるとする。
'don''t''80''s''Quark''s Bar'

   上記はそれぞれ、下記のものを表す。

don't80'sQuark's Bar

   このルールを利用して、ユーザ入力をSQL文に組み入れる直前にシングルクォート1個から2個への置き換えを行うのである。たとえば次のような置換関数を用意しておけばよい。

置換関数の例(Javaのメソッド)
class X {
public static String escape_quote(String s) {
StringBuffer buf = new StringBuffer();
for (int k = 0; k < s.length(); k++) {
char ch = s.charAt(k);
if (ch == '\'')
buf.append("''");
else
buf.append(ch);
}
return buf.toString();
}
}

置換関数の例(Perlのプロシージャ)
sub escape_quote($)
{
$_ = shift;
s/'/''/g;
return $_;
}

   これらの関数は次のように呼び出す。

置換関数の呼出し例(Java)
String parameter = ユーザが入力した値;
Connection c = データベース接続オブジェクトの取得;
Statement st = c.createStatement ();
String sql = "SELECT name, price FROM product_table WHERE code='" + X.escape_quote(parameter) + "'";
ResultSet rs = st.executeQuery(sql);


逆スラッシュの罠

   データベースエンジンの中には、Unix風のエスケープ文字である逆スラッシュ記号「\」が使用できるものがある。JISコードの環境において半角逆スラッシュは通貨の円記号「\」として表示されるので、以降の表記は円記号のほうで行うことにする。

   MySQLやPostgreSQLなどが「\」をサポートしている。これらの処理系では、上で述べたシングルクォートを2個並べる方式も使えるが、シングルクォートの直前に逆スラッシュ「\」を置く方法で、シングルクォートの特別な意味を打ち消す(エスケープする)ことができる。

置換:' → \'

   ところが、これらの処理系ではエスケープ記号「\」そのものが存在するおかげで対策が不十分になる。シングルクォートに対するエスケープ処理しか行っていないシステムがあると、たとえば次のような攻撃入力(1)は防御できるのだが、攻撃入力(2)は許してしまう。

攻撃入力(1):' UNION SELECT uid, pw FROM account_table--
→ 攻撃失敗
攻撃入力(2):\' UNION SELECT uid, pw FROM account_table--
→ 攻撃成功

   なぜなら、干渉されてできたSQL文が次のような形になるからである。

干渉されてできたSQL文
SELECT name, price FROM product_table WHERE category='\\' UNION SELECT uid, pw FROM account_table--'

   つまり、シングルクォート「'」に対する対策の「\」と攻撃者が入力した「\」が互いに中和し合い、「'」が特殊記号としての意味を持ったままになるからだ。こうしたデータベースエンジンを使用するときは次の置換も欠かせない。

置換:\ → \\


プリペアードステートメントを使おう

   SQLインジェクションには、特殊記号をエスケープ処理するよりも効果的な対策方法がある。それは「プリペアードステートメント」を使う方法である。これはユーザ入力値を埋め込む前に、SQL文の構文を確定させるものだ。構文が確定しているため、どのような入力データをもってしてもSQL文の働きを変えることはできない。

   プリペアードステートメントではほぼ通常通りSQL文を記述し、あとで値が埋め込まれることになる箇所を「?」記号で示す。この形のSQL文をいったんコンパイルして構文を確定させ、そのあとでAPI呼び出しによって「?」記号の場所に値を埋め込むのである。

   多くのプログラミング言語がこの方式をサポートしているが、Javaの場合は次のようになる。

プリペアードステートメント利用例(Java)
String parameter = ユーザが入力した値;
Connection c = データベース接続オブジェクトの取得;
PreparedStatement st = c.prepareStatement("SELECT name, price FROM product_table WHERE code=?");
// 値を埋め込む前の形のSQL文をコンパイルし、構文を確定
st.setString(1, parameter); // ? の場所に値を埋め込む
ResultSet rs = st.executeQuery();
前のページ  1  2  3   4  次のページ


セントラル・コンピュータ・サービス株式会社
著者プロフィール
セントラル・コンピュータ・サービス株式会社  長谷川 武
シニア・セキュリティ・スペシャリスト、IPA 非常勤研究員。2002年にはIPA ISEC『セキュア・プログラミング講座』の制作ディレクターをつとめた。これを契機に、現在は勤務先とそのパートナー企業を通じてセキュアプログラミングセミナー/実習/スキル評価テストといった教育サービスを「TRUSNET(R)アカデミー」として提供している。問い合わせE-mail:info@trusnet.com


INDEX
第5回:インジェクション攻撃
  はじめに
  マルチプルステートメント
シングルクォートのエスケープ
  OSコマンドインジェクション