Hatena::ブログ(Diary)

ockeghem(徳丸浩)の日記 このページをアンテナに追加 RSSフィード

[PR]WAFの導入はHASHコンサルティング
 |  

2009-03-07 どう書くorz

正規表現で「制御文字以外」のチェック 正規表現で「制御文字以外」のチェックを含むブックマーク

 一般に、セキュアコーディングの基本として入力値の検証(Validation)をせよということになっていますが、これが変な方向に行くといわゆる「サニタイズ」のような手法になってしまいます。以前も指摘したようにアプリケーションとしてのValidationは仕様に従って行うべきものです。

 ですが、概ねどの場合でも行うべき検証として以下があると思います。

 このうち後ろ二つを正規表現として書くにはどうすればいいかを考えていました。つまり、「制御文字以外の文字でm文字以上n文字以下」というようなチェックです。m文字以上、n文字以下は、{m,n}で書けるので、問題は「制御文字以外の文字」です。これはtextタイプのinput要素で、かつアプリケーション仕様としては文字種の制限をしない場合を想定しています。プログのタイトル欄や、住所欄などが典型例です。

 私は正規表現に特別詳しいわけではないので、試行錯誤的に調査して、POSIX正規表現の[:print:]を使って書けばいいのかと思いました。以下のような感じです。

  if ("this is a pen" =~ /^[[:print:]]+$/) {
    # ... これは成立する

 ところが、[:print:]は、use utf8;しないと日本語対応していないことと、utf8フラグが立った状態では、タブ、改行などにもマッチすることが分かりました。

 PHPにも同様の問題があり、文字エンコーディングUTF-8の場合は、タブ、改行などに[:print:]がマッチします。これは鬼車の仕様だそうです。wassr.jpにて、moriyoshitさんから教えていただきました。さらに、この件についての調査結果をブログにまとめてくださいました。ありがとうございます。

 さらに、Javaの場合ですと、[:print:]に相当する\p{Print}がUS-ASCIIのみに対応しているということで使えない。

 というわけで、[:print:]の使用をあきらめ、別の方法を考えることにしました。私に思いついたのは以下の二つですが、

  • 16進で指定する
  • 制御文字*以外*という指定

 16進で指定する方法は、ミスが生じやすいことと、可読性が悪いことが気になりました。うっかり、DEL(\x7f)を忘れそうだし、制御文字のうち改行やタブを許したい場合にややこしくなりそうです。Javaの場合は、サロゲートペアを含む正規表現を16進で指定する方法が分からない。

 そこで、制御文字*以外*を指定する方法として、[:^cntrl:]に着目しました。Javaの場合は\P{Cntrl}です。Perlの場合だと、次のように書けます。

  use utf8;
  #...
  if ("日本語にも対応" =~ /^[[:^cntrl:]]+$/) {
    # ... これは成立する

 テキストエリアの場合は改行を(場合によっては水平タブも)許可しないといけませんが、これも簡単に追加できます。

  use utf8;
  #...
  if ("日本語\r\n対応" =~ /^[\r\n\t[:^cntrl:]]+$/) {
    # ... これは成立する

 と、なんとかここまで漕ぎ着けたのですが、残る問題は、

  • もっといい方法はないのか?
  • Perlの場合末尾の\nがうまくチェックできない

 後者についてですが、以下のサンプルプログラムは *match* を表示します。正規表現オプションs, m, msのいずれを追加しても同じ結果でした。

my $a = "1234\n";
if ($a =~ /^[0-9]+$/) {
	print "*match*\n";
}

 Perlにとって行末の\nは空気みたいな存在なんでしょうか。Webアプリ場合、通常改行は\r\nですが、その場合はチェックできます。問題は\n(ラインフィード)だけがついている場合です。パーセントエンコードで書くと、1234%0a ですね。

 今のところ上手い方法を思いついてないので、一行用と複数行用で処理を分けることを考えていますが、もしよい方法をご存じの方があれば教えて下さい。

2009/9/24追記

ITproに、このあたりをまとめていますので参考になさってください。

no title

えむけいえむけい 2009/03/13 07:46 /^[[:^cntrl:]]+\z/

ockeghemockeghem 2009/03/15 18:35 えむけいさん、アドバイスをありがとうございます。\zについてはid:nihenさんからももご教授いだきました。まだ、小飼弾氏からは、^$ではなく、\A\zを使うように教えていただきました。\A\zが正解のようですね。これについては別途まとめたいと思います。

2009-03-02 攻撃手法を説明する意味はなにか

SQLインジェクション攻撃はDB上の任意データを盗み出す SQLインジェクション攻撃はDB上の任意データを盗み出すを含むブックマーク

 前回に引き続き、Think IT上の連載「SQLインジェクション大全」の第4回:ケース別、攻撃の手口を読んで感じたことを書きたい。

 まず、この記事は以下のような書き出しから始まっている。

本記事は、システムを防御するにはまず敵を知らなければならない、という意図の下に、攻撃手法を紹介する。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 この趣旨に異論があるわけではないが、しかしこの表現は誤解を生みやすいものだと思う。

 というのは、「敵」(攻撃方法)を知ったからと言って、防御の方法が導き出せる訳ではないからだ。例えば、開発者が「最近のSQLインジェクションの自動化攻撃にはT-SQLのDECLARE文が用いられる」という情報を得て独自に対策を考えた場合、DECLAREという単語をチェックしてエラーにしたり、単語を削除することを考えがちだと思う。現に「セキュリティ専門家」の多くがその種の対策手法を推奨してきた歴史があるわけだが、現在ではこの種の手法はサニタイズと呼ばれ、推奨されない対策となっている。本質的な対策方法を検討するには、脆弱性が混入する根本原因をつきとめなければならないが、攻撃手法を見せられただけで、その攻撃が成立してしまう根源的な原因を考えられる人は少数派だと思うのだ。

 では、攻撃手法を紹介する意味はなにか。私は三つあると考えている。

(1)攻撃がもたらすリスクを説明する

 セキュリティ対策には多少なりとも費用や時間が掛かるわけで、その費用や時間を掛けてもよいと判断をする人(経営者やマネージャ)に対して、なぜそうするべきかを納得してもらう必要がある。セキュリティ対策の場合、それを怠った場合のリスクと可能性が問題となる。「ほら、こんなに簡単に個人情報がとれるんですよ」と手法紹介やデモを見せることは、リスクや可能性を素早く納得してもらうのに有効な手段だ。

 しかし、手法はあくまで攻撃の「例」に過ぎないわけであるから、それを一般化し、発生しうるリスク全般について簡潔に説明することを忘れてはならない。

(2)開発者が対策を行うモチベーションを高める

 セキュリティ対策は開発者にとっては面倒な作業であることが多い。開発者は機能の実現にはモチベーションが高まるが、機能に直結しないチェック処理などは「面倒」で「できればやらずにすませたい」仕事だろう。ましてや、セキュリティ対策の中身が理解できていない場合には、いっそうネガティブな感情が高まる。

 開発者に攻撃手法を説明することは二つの効果がある。まず、前項と同じでセキュリティリスクを理解することで、セキュリティに対する正しい恐怖心を身につけること。もう一つは、対策の有効性を開発者として納得してもらうことだ。これらにより、セキュリティ対策を実装するモチベーションを高めることが期待される。

(3)エンターテイメントとしての攻撃手法

 攻撃手法の多くは巧妙で知的好奇心を刺激するものだ。従って,攻撃手法の説明やデモは、やり方次第では楽しいエンターテイメントになる。

 もちろん、攻撃手法を面白おかしく紹介するだけでは、手法の悪用など、良くない副作用が考えられるわけだ。しかし、一方で正しいセキュリティ知識を身につけるべき開発者がセキュリティに対して興味を持っていない現状では、「セキュリティって面白いんだよ」という啓蒙も必要だろう。

 このため「面白くてためになる」セキュリティ講座の出発点として、「エンターテイメントとしての攻撃手法」というやり方もあり得ると考えている。

 このように,攻撃手法を説明する理由を考えていくと、つきつめれば「リスクに対する正しい理解」が重要であることになる。そこで、SQLインジェクションがもつリスクとは何かをもう一度考えてみよう。私の考えるところ下記の3種類に分類される。

 以下、順に説明しよう。

情報漏洩のリスク

 SQLインジェクションの代表的なリスクが情報漏洩であるが、しかしまだ正しく理解されているわけではない。SQLインジェクションによる情報漏洩が恐ろしい理由は、データベース内の情報ならどれでも取り出しが可能な手法が分かっていることである。

 よくある誤解としては、「この画面は大した情報を扱ってないから対策しなくても大丈夫ですよ」という反応だ。たとえば、その画面はエラー番号を受け取って、エラメッセージをDBから検索して表示するだけの「つまらない」ページかもしれない。しかし、その「つまらない」ページであっても、DB内に個人情報があれば,漏洩させることが可能というのがSQLインジェクションの怖さだ。代表的な手法としては、以下の3種の方法が知られている。

 それぞれ、簡単に説明する。

(1)エラーメッセージを利用する方法

 冒頭で触れたdfltweb1.onamae.com ? このドメインはお名前.comで取得されています。で、エラーメッセージから取得できる情報として、テーブル名/カラム名/カラムの型を紹介しているが、現実にはDB内の内容も取得できる手法がある。

 具体例を示そう。まず、エラーメッセージを取得するSQLが以下のようだったとする。

select message from errmsg where errnum = $n

ここで、$nに対してSQLインジェクションを掛け、以下のようにSQLを改変する。

select message from errmsg where errnum = 1 and 1 < (select top 1 card_name + '!' + card_number + '!' + card_date from t_card)

 追加したSQLでは以下の処理を行う。

  • クレジットカード情報を保持するテーブルt_cardから、氏名(card_name)、カード番号(card_number)、有効日付(card_date)を取り出す
  • 取り出した情報を、記号「!」をセパレータとして連結する
  • 連結後の文字列を数値1と比較する

 すると、連結後の文字列は数値と比較できないので、以下のようなエラーメッセージが表示される(SQL Server 2005にて検証)。

varchar の値 'yamada!1234-5678-9012-3456!07/11' をデータ型 int に変換できませんでした。

 この例では、先頭の1レコードが得られるだけだが、SQLのハックにより、任意のレコードを得ることができる。

(2)UNIONを利用する方法

 先ほどの例と同様にSQLインジェクションにより、SQLを以下のように改変する。

select message from errmsg where errnum = -1 union select card_name + '!' + card_number + '!' + card_date from t_card

 ここでerrnum = -1としているのは、存在しないエラー番号としてである。UNIONはSQLの和集合を求める演算で、UNIONの左辺(元のSQL)と右辺(追加したSQL)をマージした結果を返す。左辺は1件もヒットしないので、結果として,新たに追加された問い合わせ結果が、このSQL全体の結果となる。この結果、以下のような値を返すことになる。

yamada!1234-5678-9012-3456!07/11

 やはり先頭一件のみの表示だが、前項と同様、任意のレコードを得る方法があり、繰り返し問い合わせることにより全てのクレジットカード情報を抜き取ることができる。

 また、UNIONを使う場合は、一覧表形式の表示箇所に用いるとより有効で、一度に複数件のレコードを得ることが可能だ。

(3)ブラインドSQLインジェクション

 今回の例ではブラインドSQLインジェクションを用いる理由はないのだが、SQLインジェクション脆弱性箇所によっては、WHERE句の真偽値のみ、あるいはSQLがエラーになったか正常終了したかのみが判別できる場合がある。このような代表例としては、ログイン画面がある。ログインが成功したか否か、あるいはログイン機能が正常終了したか異常終了したかによって、1ビットの情報が得られる。

 この1ビットの情報を積み重ねることによって、データベース内の任意の情報を取得する手法がブラインドSQLインジェクションである。詳しい手法の紹介は省略する。ブラインドSQLインジェクションは,一回のHTTPリクエストで得られる情報は1ビットなので、非常に多数のHTTPリクエストが必要となる。このため、手動での攻撃は困難で,専用のツールやスクリプトを用いることになる。

 dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。の2ページ目末尾には「スリープ機能」によるSQLインジェクションが紹介されている。

5つ目がスリープ機能を利用したレスポンス時間の差異だ。SQLインジェクションの攻撃を実施する際に、レスポンスに攻撃結果が表示されない場合、指定した攻撃が実行されたか否かを判断するために、攻撃の内容にレスポンス時間を遅くさせるための仕組みを組み入れる。レスポンス時間が遅れれば、攻撃が実行されたことがわかる。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 この手法は、WHERE句の真偽値もSQLのエラーも画面には表示されない場合の攻撃手段であり、いわば「究極のブラインドSQLインジェクション」である。画面からはなにも得られないので、問い合わせが帰ってくるまでの時間の差で1ビットの情報を得ようというものだ。ただでさえブラインドSQLインジェクションは時間がかかるところを、スリープ機能まで使うので、情報を得るのには膨大な時間がかかる。この手法は検査手法への応用としては興味深いが、現実の攻撃に用いるのは難しいかもしれない。

 そして、引用部分については、この攻撃を理解する上で必要となるブラインドSQLインジェクションの説明がないので、この説明だけでは読者はなんのことやら分からないだろう。

データ改ざんリスク

 かつて複文が利用できるデータベースの調査: SQL Serverが狙われるには理由がある - 徳丸浩の日記(2008-05-02)SQLインジェクションのサンプルが動かない - ockeghem(徳丸浩)の日記で紹介したように、一部のDBMSでは、一回の問い合わせの際に複数のSQL文を受け付ける。これを複文(multiple statement)という。攻撃手法としては、SELECT文やUPDATE文などの末尾に新たなUPDATE文を続けることによって、データの改変を行うことができる。DBMSの種類でいうと、MS SQL Serverが一番この攻撃がやりやすく、条件付きでPostgreSQLSQLiteでも可能な場合がある。さらに、MySQLでも、複文を受け付ける機能がある(PHPのmysqli_multi_queryなど)ので、元のアプリケーションがこれを使っている場合は複文による攻撃が可能だが、開発者がわざわざそのようなプログラミングをしている可能性は低そうだ。

 通常SQLの区切りにはセミコロン「;」を用いるが、SQL Serverの場合はセミコロンなしでも複文の作成が可能だ。これについては複文が利用できるデータベースの調査(2): SQL Serverが狙われるにはまだまだ理由がある - 徳丸浩の日記(2008-06-27)にて紹介した。

 データ改変の場合は、元SQLとはまったく別のSQLを追加するので、任意テーブルの任意列を書き換え可能である。最近の攻撃例では、表示内容にIFRAME要素やSCRIPT要素を埋め込むことによって、改変されたサイトを訪問したユーザにマルウェア感染させるという攻撃が行われている。

その他のリスク

 SQL Serverには、xp_cmdshellというストアドプロシジャが用意されており、SQLを利用して、DBサーバー上でWindowsのコマンドを実行することができる。xp_cmdshellはSQL Server 2000ではデフォルトで有効、SQL Server 2005以降ではデフォルトで無効である。SQL Serverを使った古いサイトでは特に注意を要する。xp_cmdshellが利用できる場合、任意コマンドの実行が可能と言うことから、DB内にとどまらない多様な攻撃が可能となる。

 また、SQL Server以外のDBMSでもファイルの読み書きが可能な場合が多い。これにより、DBサーバー上の機密ファイルをSQL経由で読み出したり、攻撃結果をファイル経由で効率よく持ち出すことが可能となるなど、ファイルを用いた攻撃が可能になる場合がある。

SQLインジェクション大全はどうか

 攻撃手法を紹介する意義について考察した後に、SQLインジェクション攻撃のリスクと一部の攻撃手法を紹介した。

 私がこのエントリを書こうと思った動機は、dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。には、一番重要となるリスクが明確に説明されていないと思ったからだ。その説明は断片的で不正確だ。その典型が2ページ目のクレジットカード情報の取り出しの説明だ。

 例えば、ECサイトデータベースクレジットカード情報の含まれる会員情報が保管されているとする。その会員向けのサービスとして、クレジットカード以外の会員情報を表示するWebページがあるとする。

 その際に使用するSQL文に、クレジットカード情報の格納されたテーブルにアクセスするSQL文を挿入して、会員名などを表示する個所に、クレジットカード番号を表示させる方法がある。

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 こう書いてあるので、引用箇所に続いて「本来画面に表示されないクレジットカード番号を表示させる」テクニックが説明されるのだろうと思うのだが、その説明はされていない。説明されているのはJOINを用いてユーザ名やクレジットカード番号を表示するSQLだ。

select u.user_name,c.card_number, c.card_date from t_users u join t_card c on u.id = c.user_id where id=’$userid’ ;

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 しかし、改変前のSQLが示されていないので、どのような攻撃の結果このSQLが得られたのかが分からない。さらに、SQLの末尾に$useridとあるので、このSQLは元々この形であったと考えるしかない。これでは、「会員名などを表示する個所に、クレジットカード番号を表示させる方法」ではない。元々クレジットカード番号を表示する画面ではないのか。

 おかしな部分はそれだけではない。$useridはその名前の示すようにユーザIDであろうが、以下のような説明があるのだ。

その時、検索のキーワードとなる「$userid」の値がブラウザから送られるPOSTやGET、Cookieの値を参照している場合、「$userid」の値を以下のように改ざんして攻撃する。

「’ or 1=1 --」

dfltweb1.onamae.com ? このドメインはお名前.comで取得されています。

 近藤氏は、よほど「' or 1=1 --」がお好きらしい。しかし、$useridの書き換えができるという前提であれば、SQLインジェクションを用いなくとも任意のユーザの個人情報を閲覧できるということである。だから、SQLインジェクション以前の話ではないのか。ユーザIDの内容を外部から書き換えられる状態は、セッション管理上のごく初歩的な脆弱性である。

 近藤氏の所属する神戸デジタル・ラボでは、Webアプリケーションソースコード診断も提供しているらしい。

また、「ソースコード診断」は、対象サイトのプログラムセキュリティチームが、手作業でチェックし、顕在化していなかった問題点を含めて明らかにしていきます。「ソースコード診断」の結果を擬似攻撃診断に反映して行うことで、相乗効果が期待でき、より精度の高い診断を実施することができます。

最高の品質|サービスの強み|プロアクティブ・ディフェンス

 先のサンプルプログラムも、公開前に自社の「ソースコード診断」を受けておけばよかった。

 なお、図2-1、図2-2、図2-3は前回(第3回)の図(文字コードの問題などの説明で本文との関連はない)がそのまま載せられているので説明が余計に分かりにくくなっている。これは編集部のミスだろうが、関係者は公開後も誰も閲覧しなかったのだろうか。

 今回の結論はこうだ。

  • 攻撃手法を紹介する目的は主にリスクを理解してもらうことである
  • 攻撃手法を説明する時は,余計な脆弱性が混入しないよう気をつけよう
  • SQLインジェクションリスクには任意データの漏洩、任意テーブルの書き換えなどがある

追記(2009/3/3 23:22)

 ようやく図2-1と図2-2が差し替えられた。

 この図によると、やはり元々から「ただのJOIN」である。(クレジットカード番号がマスクされているが、意図が不明なので追求しない)。また、SQLインジェクションの結果得られる神戸太郎さんの情報は、ユーザIDをA002からA001に変更するだけで可能だ。同様に、A003、A004と変更していくだけで、他の人の個人情報も得られる。SQLインジェクションよりも簡単だ。図が差し替えられても私の主張はなんら変更する必要はなく、より理解が容易になったと思う。

2009-02-26 訂正記事に対する補足

続:SQLのバインド機構は「エスケープ処理された値」をはめ込むのか 続:SQLのバインド機構は「エスケープ処理された値」をはめ込むのか を含むブックマーク

前回のエントリSQLのバインド機構は「エスケープ処理された値」をはめ込むのか - ockeghem(徳丸浩)の日記に対応して、mi1kmanさんのブクマ経由で、訂正が出ていることを知った。

訂正内容

1ページ目を下記のように変更いたしました(2個所)。

バインド値はエスケープ処理した後にプレースホルダにはめ込むので、悪意あるSQL文が挿入されても、その実行を阻止することができる(図1-2)。

SQL文のひな型とバインド値は個別にデータベースに送られ、構文解析されるので、バインド値に悪意あるSQL文が挿入されても、その実行を阻止することができる(図1-2)。

no title

なんとなく私のエントリの断片が散りばめられているのを別にしても、『悪意あるSQL文…の実行を阻止することができる』というあたりに、サニタイズ的発想が依然として感じられる。悪意があろうがなかろうが、淡々と「どんな入力に対しても正しく対応する」というプログラムを書きさえすればSQLインジェクションは防げるのであるから、『実行を阻止』などという仰々しい表現を用いる必要はないのだ。こういう表現はIPSやWAFに対して用いるべきだろう。

 また、私が指摘していなかった箇所についても変更が行われている。指摘があったのだろう。

しかし、LIKE句を含むSQL文などについてはバインド機構の適用に注意を要する。これは、「%」や「_」といったワイルドカード文字がバインド機構によってエスケープされないため、割り当てる変数の内容によっては予想外の問い合わせ結果が返ってくる可能性があるからだ。この場合、バインド機構に変数を割り当てる前に、エスケープ処理を使用して、変数に格納されている文字列に含まれるワイルドカード文字をエスケープする必要がある。

SQLステートメントを書く時に必ずバインド機構を使用すれば、バインド機構に不具合がない限り、SQLインジェクションは不可能だと考えられる。

ただし、データベースの種類によっては、バインド機構を使用した場合、LIKE句やユーザー定義関数などの引数にバインド値を指定すると、開発者の思惑どおりの動きをしない場合がある。そのため「%」や「_」といったワイルドカード文字がバインド値として送られないように値検証処理でエラーにしたり、エスケープしたりする必要がある。

有効な対策方法はデータベースの種類によって異なるのでベンダからの情報を参照していただきたい。

no title

 元の表現よりはマシだが、まだ誤解を招きやすいと思う。

 LIKE句のワイルドカードをエスケープする理由は、「%」などがワイルドカードとしての「%」なのか、検索文字列の一部としての「%」という文字なのかを区別するためだ。だから、『ワイルドカード文字がバインド値として送られないように』という表現はおかしい。ユーザにワイルドカード指定を許しているサイトであれば「%」などのエスケープは不要の場合もあるし、許していない場合はescapeなどによりエスケープする必要がある。これはアプリケーション仕様によるところなので、「おまかせ」にはできないのだ。

 また、『有効な対策方法はデータベースの種類によって異なる』のは確かだが、『SQLインジェクション大全』と大きくでるからには、主要DBについては、ベンダー固有情報にも踏み込むべきではなかったか。

その他の指摘

ついでなので、その他の指摘をいくつかしておきたい。

(1)図1のSQLのWHERE句にシングルクォートが残っているのがおかしい。

(2)エスケープを実行する場所

エスケープ処理は、SQL文を生成する直前に実施するのが定石である。入力直後にエスケープ処理を行うと、SQL文を生成する直前に別の文字列操作が行われた場合、SQLインジェクションが成功する文字列が完成してしまう可能性があるからだ。

 このような場合もあるだろうが、それ以前に、プログラムの内部では入力データそのままで扱わないと不便で仕方ないだろう。処理のたびにアンエスケープしなければならない。たとえば、表示する場合はどうするのか。近藤氏は、「攻撃を受けない」ことばかり考えているようだが、アプリケーション開発で考慮すべきことはセキュリティ以外の方がずっと多いのだ。


(3)エスケープ対象の文字

なお、エスケープ処理の必要がある文字列はデータベースサーバーによって異なるので、利用環境に合わせて実施しなければならない

これは、『文字列』ではなく『文字』


(4)エラーメッセージ

 2ページ目。

アプリケーションで作成したエラーメッセージを表示するのはよいが、エラーメッセージに必要以上の情報を表示したり、状態に応じてエラーメッセージの種類や表示内容を変えたりしてはいけない。攻撃者はそれらの情報を観察し、ブラインドSQLインジェクションを仕掛ける可能性があるからだ。ブラウザ上には、処理が失敗したことが分かる程度の簡単なエラーメッセージを表示し、詳しくはログファイルなどに出力し、ユーザーに見せないようにすべきである。

概ねよいのだが、『ブラウザ上には、処理が失敗したことが分かる程度の簡単なエラーメッセージを表示』という部分がよくない。処理が『失敗した』ことが分かれば、攻撃に対するヒントになり、ブラインドSQLインジェクションに利用される場合もあるだろう。処理が失敗したのか、入力値検証ではじいたのかを含めて、もっと大雑把なメッセージにしたほうがよい。


(5)都道府県コード

あるいは都道府県を「01〜48」のコードで表している場合は、「01〜48」の文字列のみを有効とするなどである。

 日本には、47の都道府県があるのだから、説明なしに「01〜48」とすると読者は面食らう。私の身の回りでは、48番目の県について「海外を示す」とか「いや、ぷりぷり県だ」とか、さまざまな憶測があったが、説明がないので本当のところは不明だ。


(6)マルチバイト文字について

例えば、MySQLPostgreSQLは「\」をエスケープする必要がある。

 デフォルトの挙動としてはそうなのだが、PostgreSQLの場合standard_conforming_stringsというパラメータをonにすると、「\」のエスケープはしなくてもよい。SQLインジェクション対策という点では、onの方が安全なので、『「\」をエスケープする必要がある』と断言するのではなく、standard_conforming_stringsについても言及するべきではないか。

 一方、MySQLにもNO_BACKSLASH_ESCAPESという同種のオプションがあるが、比較的最近追加されたオプションで、私は試したことがない。こちらは、実績があまりなさそうで、推奨まではできにくそうだ。


(7)5C問題が発生する文字エンコーディング

このような現象はShift_JISにのみ起きる訳ではなく、すべての文字コードにおいて発生しうる

 『このような現象』の指す内容が曖昧だが、直前に指摘していた0x5Cの問題のことを指すのであればShift_JIS系列の文字エンコーディング(cp932などを含めて)に固有の問題といっていい。EUC-JPUTF-8などではこの問題はないのだ。そして、それが文字エンコーディングとしてShift_JISを避けたほうがよいことの根拠になっている。

 技術文書の一般論として、曖昧な内容を「すべて…発生しうる」と断言するのは控えるべきだろう。


 まだ指摘したい内容はあるが、意見の違いに属するようなものは排除し、根拠が明確に示せるものに絞った。読者の参考になれば幸いである。

参考:WASForum Conference 2008講演資料「SQLインジェクション対策再考」

2009-02-21 劣化コピーされる「安全なウェブサイトの作り方」

SQLのバインド機構は「エスケープ処理された値」をはめ込むのか SQLのバインド機構は「エスケープ処理された値」をはめ込むのかを含むブックマーク

以前このブログでも取り上げたことのある神戸デジタル・ラボの近藤伸明氏がThink IT上で「SQLインジェクション大全」という連載を執筆しておられる。その第三回「SQLインジェクションの対策」を読んで以下の部分が引っかかった。

バインド機構とは、あらかじめSQL文のひな型を用意し、後から変動個所(プレースホルダ)に実際の値(バインド値)を割り当ててSQL文を生成するデータベースの機能だ。バインド値はエスケープ処理した後にプレースホルダにはめ込むので、悪意あるSQL文が挿入されても、その実行を阻止することができる(図1-2)。

http://thinkit.jp/article/847/1/

 たしかにエスケープ処理を使ってバインド機構を実装する場合もある。no titleから派生して、MySQL+JDBCの組み合わせでは、デフォルトではPreparedStatmentとしてクライアント・サイドのプリペアードステートメントが使われることが分かった。このあたりの話題は Connector/J 5.1とServer Side Prepared Statement - mir the developerが詳しい。そちらをお読みいただくとして、クライアント・サイドのプリペアードステートメントとしてバインド機構を実装している場合は、先に引用したように、「バインド値はエスケープ処理した後にプレースホルダに」埋め込まれる。

 しかし、このような挙動は私の期待とは異なるし、私の周囲のブックマークwassr.jpの書き込みなどを見ても、「バインド機構はサーバーサイドのプリペアードステートメントを使うもの」という意識の方が多かったように思う。

 サーバーサイドのプリペアードステートメントの場合は、エスケープ処理は行われない。その代わり、プレースホルダ記号が置かれたままのSQL文とバインド値が個別にサーバー側に送られる。サーバー側ではSQL文を構文解析するが、その際にキャッシュの内容を確認して、既にキャッシュされたものであれば、構文解析処理を省略してキャッシュの方を利用する。このあたりの解説とメリットについては JDBC接続を高速化する− PreparedStatementキャッシュの威力−:事例に学ぶWebシステム開発のワンポイント(11) - @ITが分かりやすい。すなわち、サーバーサイドのプリペアードステートメントを使うと、以下のようなメリットがある。

 SQLインジェクション対策には大きくバインド機構とエスケープの2種類の方法があるが、第1選択肢としてバインド機構が推奨される理由は、この「原理的にSQLインジェクションの可能性がない」ことによるものだと考える。つまり、サーバーサイドでないとその意味がない。その至近の例として、先に参照した「U+00A5によるSQLインジェクション」についても、クライアント・サイドのプリペアードステートメントでは脆弱性の可能性があり、サーバーサイドのプリペアードステートメントでは問題がなかった。このため、引用した「エスケープ処理した後にプレースホルダに」という部分に引っかかったわけだ。

 ところで、バインド機構の説明は他の文献ではどうなっているのだろうか。日本で一番権威のある「安全なウェブサイトの作り方 改訂第3版」について調べてみた(同書P7)。

1)-1 SQL 文の組み立てにバインド機構を使用する

解説

これは、SQL 文が注入される原因を作らない実装です。バインド機構とは、実際の値がまだ割り当てられていない記号文字(プレースホルダ)を使用してあらかじめSQL 文の雛形を用意し、後に実際の値(バインド値)を割り当ててSQL 文を完成させる、データベースの機能です。バインド値はエスケープ処理されてプレースホルダにはめ込まれるため、利用者に入力された悪意あるSQL 文の実行を防ぐことができます。

http://www.ipa.go.jp/security/vuln/websecurity.html

 意外なことに、「安全なウェブサイトの作り方」でもエスケープ処理云々という記述がある。もう一つ興味深いことに、先に引用したThink ITの引用部分と酷似しているというか、少なくともかなり似ている。Think ITの解説末尾では【参考文献】として安全なウェブサイトの作り方を参照しているので、悪意的なものではないにしても、誤解を招きやすい行為とは言えるだろう。ここまで似てしまうのであれば、いっそ引用してしまえばよかったと思う。

 では、類似の内容は他所にはないのか。簡単に調べてみた。以下の二カ所が見つかった。

SQLインジェクションSQL Injection)の対策

(2)バインド機構の使用

・バインド機構とは、あらかじめSQL 文の雛形を用意し、後から変動箇所(プレースホルダ)に実際の値(バインド値)を割り当ててSQL 文を生成するデータベースの機能です。

・バインド値はエスケープ処理した後にプレースホルダにはめ込むので、悪意あるSQL 文が挿入されても、その実行を阻止することができます。

参考文献:IPA:安全なウェブサイトの作り方

http://column.proactivedefense.jp/topics/2008/03/sqlsql-injection.html

バインド機構(バインドメカニズム)とは、あらかじめSQL文のひな形を定義しておき、プレースホルダーを使うことで実際の値を割り当ててSQL文を完成させる、という仕組みです。プレースホルダーに値が入るときには、SQL文の組み立てに必要なエスケープ処理が行われるので、悪意のあるSQL文の組み立てを防ぐことができます。そのためSQLインジェクション対策として利用することが推奨されています。

[http://脆弱性診断.jp/words/w-sql/data/000059.html]

 脆弱性診断.jpの方は、「安全なウェブサイトの作り方」を参照していないので、より紛らわしい行為ということができるかもしれない。

 結論はこうだ。

  • 「安全なウェブサイトの作り方」に示されているバインド機構の説明は必ずしも正確ではない
  • その正確でない説明が劣化コピーされてばらまかれている
  • IPAが公表している文書でも特記されていない限り著作権がある。引用など基本的なルールを守ろう

参考:WASForum Conference 2008講演資料「SQLインジェクション対策再考」

2009-02-05 再びホワイトリストとブラックリスト

書籍『Ajaxセキュリティ』に関する残念なお知らせ 書籍『Ajaxセキュリティ』に関する残念なお知らせを含むブックマーク

 昨年の10月に刊行された書籍Ajaxセキュリティは,発刊直後に購入したが,しばらく積ん読になっていた。最近になって読み始めたのだが,いささかあきれる結果となった。HPの現役エンジニア2名の著作,一人は元SPI Dynamics社(WebInspectの開発元,HPが買収)出身,GIJOE氏の監訳ということで期待していたのだが,残念である。

 残念だと思う主要な理由は,脆弱性への対策が十分に示されていないことだ。Ajaxであってもインジェクション系脆弱性が発生する可能性があること,むしろ従来型のWebアプリケーションよりもその可能性が広がることは説明されているが,肝心の対策が不十分だ。

 本書第四章の後半には,対策として入力検査(バリデーション)が示されている。

  • 4.6 適切な入力検査
  • 4.7 リッチなユーザ入力のバリデーション

 しかし,入力検証だけでは,任意の文字入力を許す場合の対策はできない。入力検証は、アプリケーション仕様に基づいて検証するのであるから,仕様として「<」,「>」,「"」,「'」,「/」などの入力を許可する場合には,検証ではじくわけにはいかないからだ。

 現実問題として,英米人の名前にはアポストロフィ「'」がつく場合がある(例えばM'Intosh)。これら記号の処理をどうせよと説明しているのだろうかと思って本書を読み進めたところ,ちょうどそのテーマの説明があった(P113)。

 しばしば問題となるのは,アポストロフィの取り扱いです。英語圏のユーザにとって,氏名や固有名詞などでアポストロフィを入力できる方が便利です。しかし、ユーザの入力にアポストロフィの仕様を許可する場合,インジェクション攻撃を防ぐにはどうすればよいでしょうか。

 解決策は,ホワイトリストパターンの改良を続けることです。ユーザの姓として,O'Brienなら有効な値でしょうが,'SELECT * FROM tblCreditCardsはまず違うでしょう。単語の数(正規表現の用語では,空白文字で区切られた,英数字からならるグループの数)を制限することも検討して下さい。使用が許されるアポストロフィの個数を制限するのも,1つの方法です。複数のアポストロフィが含まれる名前など,まずありえないからです

これに対して,訳註がついている。

訳註:名前のフィールドにはアポストロフィを1つだけ許可するとしても,それを保存するSQL文や表示するHTML/XMLではアポストロフィを正しくエスケープしなければ,Webアプリケーションとして機能しないのは当然である。しかし,後の章で説明する多層防御のうちの1レイヤとしてであれば,この入力検査も無意味なわけではない。

 訳註の第1センテンスは極めて妥当な,というより当然の内容である。しかも,このエスケープがあれば,バリデーションがなくてもインジェクション対策になっているのだ。だとすれば,インジェクション対策の原則として,まずエスケープの方を説明すべきであるが,本書にはHTML/XML/JavaScript(JSON)/SQLなどのエスケープが説明されていないようだ。これでは,そもそも特殊記号を含む文字列の正しい処理ができない。

 また,「解決策は,ホワイトリストパターンの改良を続けることです」という説明もおかしい。ホワイトリストの許容範囲を広げていくと,現実にはホワイトリストではなく,どんどん「グレーリスト」になってしまうのだ。その典型が,「使用が許されるアポストロフィの個数を制限する」という説明だ。SQLインジェクションアポストロフィ(シングルクォート)1つで十分攻撃できる。現に,引用箇所の少し前に例示された「'SELECT * FROM tblCreditCards」は,アポストロフィは1つだけだ。筆者は自己矛盾を感じなかったのだろうか。

 実際には,「アポストロフィ1つまでは許可する」というのはホワイトリストではない。「アポストロフィ2個以上が入った文字列は攻撃と見なす」というブラックリストを裏返しただけのことで,実質的にはブラックリストなのだ。ホワイトリストブラックリストの区別がつかない「セキュリティ専門家」は,日本だけでなく,欧米にもいるようだ。

 というわけで,本書は読むべき箇所がない訳ではないのだが,肝心なことが説明されていなくて役に立たないと思った。その前に読んだAjaxアプリケーション & WebセキュリティAjaxセキュリティに関しては散々な出来で,滅多に専門書を手放さない私も,Amazonマーケットプレイスで売ってしまった。

 Ajaxは技術が急速に普及した一方で,セキュリティに関するまとまった解説が見あたらない。良書が少しでも早く発刊されることを切望する次第である。

 |  
Google