[PR]小規模ECサイトに最適なWAF、SiteGuard Lite

徳丸浩の日記


2008年06月01日 そろそろSQLエスケープに関して一言いっとくか

_SQLのエスケープ再考

本稿ではSQLインジェクション対策として、SQLのエスケープ処理の方法について検討する。

最近SQLインジェクション攻撃が猛威を振るっていることもあり、SQLインジェクションに対する解説記事が増えてきたようだが、対策方法については十分に書かれていないように感じる。非常に稀なケースの対応が不十分だと言っているのではない。ごく基本的なことが十分書かれていないと思うのだ。

SQLインジェクション対策には二通りある。バインド機構を使うものと、SQLのエスケープによるものだ。このうち、SQLのエスケープについて、十分に書かれているテキストが見当たらないのだ。このため、自分で書いてみようと思う。

IPAの「安全なウェブサイトの作り方改訂第三版」ではSQLのエスケープについて以下のように説明されている。

1)-2 バインド機構を利用できない場合は、SQL 文を構成する全ての変数に対しエスケープ処理を行う

解説  これは、根本的解決 1) のバインド機構を利用した実装ができない場合に実施すべき実装です。
 利用者から入力されるパラメータや、データベースに格納された情報などに限らず、SQL 文を構成する全ての変数や演算結果に対し、エスケープ処理を行ってください。エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字(たとえば、「'」→「''」、「\」→「\\」 など)です。
 なお、SQL 文にとって特別な意味を持つ記号文字は、データベースエンジンによって異なるため、利用しているデータベースエンジンに応じて対策をしてください。データベースエンジンによっては、専用のエスケープ処理を行うAPIを提供しているものがあります(たとえば、Perl ならDBIモジュールのquote()など)ので、それを利用することをお勧めします。

引用した部分は、一般的な内容を網羅しているものの不満もある。短い文章の中で「など」が3回も現れることに象徴されるように、あいまい性の残る文章となっている。これはデータベースの製品依存のところでやむを得ないというのはよく理解できるのだが、結果として、読者に「データベースエンジンのマニュアルを読め」と言っているの等しい。それでいて、読者には、マニュアルのどこをどう調べたらよいかまでは示していない。「エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字」と書かれているが、この説明だと、「;」や「=」、空白までエスケープしなければならないと誤解する読者が出てくるかもしれない。実際には、これらの文字をエスケープする必要はないし、SQLの標準規格には、そもそもこれらの文字をエスケープする手段が用意されていない。エスケープの必要がないので手段も用意されていないのだ。

そこで、「安全なウェプサイトの作り方」を補完するような形で、もう少しSQLエスケープについて書き足してみたい。

SQLインジェクションのおさらい

SQLインジェクション攻撃では、SQLに渡すパラメータ部分にSQL断片を挿入し、SQLの意味を書き換えることによって行われる。 バインド機構を使わずに自前でSQLを組み立てる場合、SQLに対するパラメータはSQLのリテラル(定数)の形で渡される。

リテラルには複数の型があるが、通常問題になるのは数値と文字列である。

数値リテラルの場合:

SELECT * FROM XXXXX WHERE NNUM=●●●      -- ●●●は数値、例えば 123

文字列リテラルの場合:

SELECT * FROM XXXXX WHERE EID='■■■'      -- ■■■は文字列、例えば S853

なぜリテラルを構成する文字列(●●●や■■■)を操作することでSQLの構文まで変わるのか。それは特殊記号などでリテラルを終端させ、その後にSQL断片を埋め込むからだ。

数値リテラルを終端させるには、数値以外の文字を加えればよい。

例: 123OR TRUE         -- 数値リテラル123の後に、OR TRUE が続く。123 OR TRUEと同じ

文字列リテラルを終端させるには、単一引用符「'」を加えればよい

例: A'OR'A'='A

この例を先のSQLに適用すると、

SELECT * FROM XXXXX WHERE EID='A'OR'A'='A'

となる。つまり、WHERE句は常に真となる。

SQLインジェクション対策の基本的な考え方

すなわち、SQLインジェクション対策の方針としては、リテラルを勝手に終端させないようにすることが必要なのだ。そして、この処理は数値リテラルと文字列リテラルとでは、方法が異なる。

数値リテラルの場合は、数値以外の文字が出てきた時点でリテラルの終端となるので、数値としての妥当性検証を行うことになる(数値項目に対するSQLインジェクション対策のまとめ参照) 。

一方、文字列リテラルの場合は、文字列リテラルのエスケープを行えばよい。SQL92などSQLの標準規格で規定しているのは、単一引用符「'」のエスケープであるが、データベースの種類によっては、円記号(バックスラッシュ)「\」のエスケープも必要となる。

商用データベースの場合

商用データベースの場合は、SQL標準に従い、単一引用符「'」を「''」と重ねる処理を行う。OracleSQL ServerIBM DB2についてはリファレンスと動作の両方で確認した。

オープンソース・データベースの場合

オープンソースのデータベースとして広く普及しているMySQLの場合、文字列リテラル中にC言語風の「\」を使ったエスケープシーケンスが記述できる。従って、文字列リテラル中に「\」自体を記述する際には、「\\」とする必要がある。一方、単一引用符「'」は「''」としてもよいし、「\'」としてもよい。その他、二重引用符「"」が利用できるなど自由度が高い。

オープンソース・データベースのもう一方の雄PostgreSQLの場合は事情が少し複雑となるが、デフォルトではMySQLと同じで、単一引用符と円記号(バックスラッシュ)の両方をエスケープする必要がある。しかし、設定パラメータstandard_conforming_stringsがonの場合(デフォルトはoff)は、Oracleなどと同じ挙動となる(現実にはもう少し複雑だが稿をあらためて説明したい)。

まとめると以下のようになる。

データベース 元の文字 エスケープ後
Oracle
MS SQL
IBM DB2
' ''
MySQL
PostgreSQL
' '' または \'
\ \\

なお、念のため補足すると、エスケープ処理はセキュリティ対策のために行うものでは元々なく、与えられたパラメータに対して正しく処理を行うために必要な処置である。入力に「'」や「\」が使えないと不便で仕方がないし、現実的に不具合が生じるだろう。

その他のデータベース製品の場合はどうしたらよいか

今回説明した内容にもっとも近い記述があるドキュメントとしては、佐名木智貴氏の近著「セキュアWebプログラミングTips集(ソフト・リサーチ・センター)」がある。同書では、SQLエスケープの基本として

「'」は、「''」(シングル・クォート2個)にエスケープ処理することで、SQLインジェクションから防御することができる(同書P210)

とした上で、

mySQLとPostgreSQLの場合のSQLインジェクション対策として、入力データをSQL文の文字列リテラルとして使う場合、「'(シングル・クォート)」と「\」をSQLエスケープすること(同書P213)

と指摘している。

非常に丁寧な仕事ぶりで好感を持った。ただ、同じページの以下はいただけない

筆者の知らないデータベース・ソフトウェアでは、SQLが拡張され、それ以外のメタキャラクタもあるかも知れない。ぜひ読者諸氏には今一度、自分の使っているデータベース・ソフトウェアのSQLリファレンスを通読することを推奨する(同書P213)。

「SQLリファレンスを通読」とは・・・無茶言うなよと思う。

実際には通読する必要はなく、「リテラル」、「定数」、「文字列」などのキーワードを手掛かりに、文字列リテラルの項を探すとよい。本稿で引用したリファレンスもこのようにして探したものである。

Oracleの場合を例にマニュアルの見方を説明しよう。題材として、Oracle10gのオンラインマニュアルを利用する。

まず、目次から「リテラル」を探すと、「2 Oracle SQLの基本要素」に「リテラル」や「テキスト・リテラル」という項が見つかる。「テキスト・リテラル」の項を読むと、

  • cは、データベース・キャラクタ・セットの任意の要素です。リテラル内の一重引用符(')の前には、エスケープ文字を付ける必要があります。リテラル内で一重引用符を表すには、一重引用符を2つ使用します
  • ''は、テキスト・リテラルの始まりと終わりを示す2つの一重引用符です。

このように、エスケープの必要な文字は一重引用符「'」であること、エスケープの仕方は一重引用符を2つ使用することであることがわかる。他のデータベース・ソフトウェアでも、同様に探すことができるだろう。

本稿を参考に正しいSQLインジェクション対策を実施していただきたい。


続く(徳丸浩の日記 - SQLインジェクション対策 - SQLエスケープにおける「\」の取り扱い)

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

追記(2010/03/26)

このエントリを書いた後、IPA非常勤研究員として、SQLインジェクションの正しい対策方法について調査・検討しました。その成果は「安全なSQLの呼び出し方」という冊子(安全なウェブサイトの作り方別冊)という形にまとめられました。ぜひご活用いただければと思います。ダウンロードはこちらから。

本日のツッコミ(全3件) [ツッコミを入れる]

_ 佐名木 [mySQL での「\」のエスケープについて知ったのは、(私にとっては無知の知の領域にある)mySQL のリファレンス..]

_ momo [クオートをエスケープした内容をSQLに格納するのはいいが、次にDBからその内容を利用するとき、エスケープは消え、クオ..]

_ Nkzn [↑セカンドオーダーSQLインジェクション?っていうんでしたっけ?]


2008年05月02日 複文が利用できるデータベースの調査

_SQL Serverが狙われるには理由がある

SQLインジェクションを利用したWebサイトの改ざん事件が頻発している。標的となったサイトの多くがIIS(ASP)とSQL Serverの組み合わせを利用していることから、今回の攻撃がIISやSQL Serverの脆弱性を利用したものだと言う報道があるらしい。
これに対して、マイクロソフトが反論している。 「SQLインジェクション攻撃とIISは無関係」とMicrosoft

 しかしMicrosoftはセキュリティ対策センター(MSRC)のブログで、今回のWebサーバ攻撃では「未知の脆弱性や新しい脆弱性は悪用されていないことが当社の調査で分かった」と説明。攻撃はIISやSQL Serverの脆弱性を突いたものではなく、アドバイザリー951306の脆弱性とも無関係だとした。

これはまぁ、もっともな内容だと思う。しかし、疑問は残る。なぜ、IISとSQL Serverの組み合わせが狙われるのか。これに対する理由は色々と憶測できるが、私はSQLインジェクションによるデータ改ざんを行う上で重要となる「SQLの複文」に注目している。 SQLインジェクションによるサイト改竄 2008-03-20 - T.Teradaの日記によると、 Neil Carpenter’s Blog : Anatomy of a SQL Injection Incident, Part 2: Meatを引用する形で、今回の攻撃を以下のように説明している。

非常におおざっぱに言うと、以下のような感じです。

1. リクエストを送ってみて、SQLインジェクション脆弱性があるか調べる。
2. 脆弱性がある場合、それを突いて、全テーブルの、全カラムの、全レコードの値を改竄する*1。具体的には、DB格納値の末尾に「<script src=http://www.211796...(省略)」のような攻撃コードを追加する。
3. DBのデータをエスケープせずにHTMLに出力している箇所があると、挿入されたJavaScriptがユーザのブラウザ上で動作して、これが悪さをする。

この2番目のフェーズでは、T-SQL(Transact-SQL)というSQL Serverの拡張SQLが利用されているのだが、SQLインジェクションにより以下のようなリクエストでこれを実行している。

d=z';DECLARE%20@S%20NVARCHAR(4000);SET%20【略】

すなわち、セミコロンにより複数のSQLを記述する「複文」が利用されている。

ところで、SQL Injection Cheat Sheetという文書によると、言語とデータベースの組み合わせにより、この複文が利用できるか否かが異なっている。

SQL Server MySQL PostgreSQL Oracle
ASP
ASP.NET
PHP ×
Java ×
凡例:
○:複文に対応
×:複文に非対応

ごらんのように非常に不完全な表であるので、手元の環境で補完してみることにした。ただし、自宅のPCがWindowsXP Home Editionなので、IISを実行することができない。そのため、ASPとASP.NETは調査から除外し、代わりににPerlを追加して、SQL Server、MySQL、PostgreSQl、Oracleについて、調査を行った。

SQL Server MySQL PostgreSQL Oracle
PHP × ×
Java × ×
Perl × ×
凡例:
○:複文に対応
△:更新系のSQLであれば複文に対応
×:複文に非対応

事前の予想では、言語依存もあるのかなと思っていたのだが、確認してみると言語依存はほとんどなく、データベースの種類によって決まるようだ。

SQLインジェクションによって複雑なことを行う(とくに更新系)ためには、複文が利用できるかどうかが問題となる。広く利用されているデータベースでは、SQL ServerとPostgreSQLで複文が利用できることは、攻撃者の立場から考えると、ターゲットにしやすいプラットフォームということになる。PostgreSQLは日本では人気があるが、世界的には他の三種のデータベースほど普及していない。このように考えると、SQL Serverが狙われる背景には、(Microsoftの責任とは言えないにしても)、それなりの理由があるのだと考える。


2007年12月10日 イメージファイトのまとめ

_画像ファイルによるクロスサイト・スクリプティング(XSS)傾向と対策

最近、画像ファイルを用いたクロスサイト・スクリプティングが注目されている。本稿では、画像を悪用したXSSについて説明した後、対策方法について解説する。

画像によるXSSとはどのようなものか

Internet Explorer(IE)の特性として、コンテンツの種類を判別する際に、レスポンスヘッダ内のContent-Typeだけでなく、コンテンツの内容も判断基準にしている。このため、Content-Typeが例えばimage/gif(GIF画像)となっていても、中身がHTMLであればHTMLと解釈して表示する。このため、JavaScriptが埋め込まれていたら、JavaScriptを実行することになる。

例を示そう。このファイル(html.gif)は、拡張子は.gifなので、ダウンロードするとContent-Typeはimage/gifとなるが、内容はHTMLでありJavaScriptが埋め込んである。これをIEとFirefoxで表示した例を以下に示そう。

IE7での表示
Firefoxでの表示

IEが、状況によってはContent-Typeを無視して、画像をHTMLと解釈し内部に埋め込まれたJavaScriptを実行してしまう。これによるXSSが成立する場合がある。典型的な状況とてしは、画像のアップロード機能を有するアルバム・サービスのような場合だ。たとえば、はてなの画像アップロードシステムには、この脆弱性があったとして、対応がなされたことがある。

下図は、画像によるXSSの模式図である。攻撃者(左側のサングラスの人物)は、攻撃用JavaScriptを仕込んだ画像を、maitter.com画像アップロードシステムを利用して登録・公開し、この画像のURLをダウンロードするよう、メールやブログなどで誘導する。被害者がこの誘導に引っかかって画像を閲覧すると、JavaScriptが実行され、maitter.comドメインのCookieが盗難されるなどの被害にあう。


先に示したhtml.gifは内容はhtmlテキストだけという素朴なものだが、イメージそのもの、パレット、コメントなどにhtmlやJavaScriptを仕込む手法が知られている(*1)。

対策の検討

IEがコンテンツの種類をどのように判別しているかは公開されていないようだが、たとえば2007年10月10日に公開されたマイクロソフト セキュリティ情報 MS07-057には以下のような記述がある。

PNGs が MIME スニッフィング アルゴリズム の jpg/gif と同じ状態に昇格されます。例: サーバーが png のコンテンツの種類を送信した場合、Internet Explorer は magic-byte (マジックバイト) のテストを実行し、成功した場合、権限のあるサーバーの MIME の種類として処理します。

意味のとりにくい日本語(機械翻訳?)だが、どうもコンテンツの種類判別にIEがマジックバイトを利用しているらしいことがわかる。

マジックバイトとは

マジックバイトとは、画像などのフォーマットを判別しやすいように、ファイルの先頭の数バイトに置かれた固定のデータである。GIF形式の場合であれば、"GIF87a"あるいは"GIF89a"で始まることになっており、先頭6文字を見ればGIF形式であることが容易に判別できる。下表に、主な画像形式のマジックバイトを示そう。

画像形式 BMP GIF JPEG PNG
マジックバイト BM GIF8[79]a \xFF\xD8 \x89PNG\x0D\x0A\x1A\x0A

IEのコンテンツ種類判別はContent-Typeとマジックバイトの組み合わせ

以上の情報などから、どうもIEのコンテンツ種類判別は、Content-Typeとマジックバイトの組み合わせにより決まるように思える。これを検証するために、主要な画像フォーマット4種(BMP、GIF、JPEG、PNG)のContent-Typeとマジックバイトの全ての組み合わせを試してみた。検証用に用いたサンプルデータは、マジックバイトとJavaScriptを組み合わせただけの簡単なものである。一例を下図に示す(GIF形式の偽画像)。


結果は下表の通りである。表で、「HTML」となっている欄はHTMLと判別され、JavaScriptが起動するもの、「画像」となっているのは、画像と解釈しようとしたが表示できないため「×」表示となることを示している。これらはリンクとなっていて、実際のサンプルデータで試すことが出来るようにした。

画像種類マジックバイトContent-Type
image/bmpimage/gifimage/jpegimage/png
BMPBMHTMLHTMLHTMLHTML
GIFGIF87aHTML画像HTMLHTML
JPEGxFFxD8HTMLHTML画像HTML
PNG\x89PNG\x0d\x0a\x1a\0aHTMLHTMLHTML画像(*2)

この表から分かることは以下の二点である

  • Content-Typeとマジックバイトは同一の画像フォーマットに対応したものでなければならず、不一致だとHTMLとみなされる(可能性がある)
  • BMP形式に関しては、Content-Typeとマジックバイトの両方を揃えても、なおHTMLとみなされる場合がある

なお、マジックバイトのContent-TypeからPNG形式を判定するようになったのは、前述のようにMS07-057(2007年10月10日)適用後のことであるので、ちょうど2ヶ月前からのことである。

対策:ではどうすればよいか

従来、画像XSSの対策には、イメージを他の形式に変換してから元の形式に戻す、あるいは竹迫氏のmod_imagefightのように画像中にXSS防御文字列を挿入するなど、さまざまな手法が提案されている。しかしながら、対策として不十分であったり、画質や処理速度の低下を招くなどさまざまな副作用があった。

マイクロソフトがMS07-057にてPNG画像のコンテンツ種別判定を改善してくれたことから、Content-Typeとマジックバイトの判定のみで、画像XSSを予防できる可能性が高くなった。

これでもまだ、BMP形式については改善がされておらず、画像XSSの危険性が残っている。コレに対しては、BMP形式画像のアップロードを許容する場合はPNGに変換して公開するというガイドラインを提案したい。BMP形式は通常画像が圧縮されていないので圧縮して公開した方が好ましいこと、BMPの特性(色数、可逆性など)はPNGで全てカバーしていることがその根拠である。

結論としては以下のようになる。

  • 画像のマジックバイトとContent-Typeが同一の画像フォーマットに対するものであることを確認する
  • MS07-057を適用する(ようにユーザに促す)
  • 例えば、BMPを避ける。あるいはPNGに変換して公開する。



謝辞:
画像埋め込みのHTML/JavaScriptについては、 はせがわようすけ氏からさまざまな情報を教授いただいた。記して、感謝の意を表する。


脚注
*1:JavaScriptを埋め込んだ画像を作ってみました参照
*2:MS07-057を適用していないとHTMLと解釈される


参考文献:
マイクロソフト セキュリティ情報 MS07-057
2007-07-15 - T.Teradaの日記 - 画像へのPHPコマンド挿入
TAKESAKO @ Yet another Cybozu Labs: LL魂お疲れ様でした[LLSpirit]
yohgaki's blog - 画像ファイルにPHPコードを埋め込む攻撃は既知の問題

本日のツッコミ(全3件) [ツッコミを入れる]

_ はせがわ [はてなでの2004年の対策は、MS07-034のmhtml:に対する対策だったはずです。mhtml:をURLに付与す..]

_ hoshikuzu [まさしくアレはmhtml対策でした。はてなに報告した人より。]

_ えむけい [IE8でサンプルデータを試してみたらBMPはすべてtext/plainで(JavaScriptは起動せず)、GIF/..]


2007年12月06日 イメージコピー版イメージファイト

_うまく動いてないようですが

cybertさんのブログから

IE仕様?をついた画像ファイルを使用したXSSというのがあります。なかなか決定的な対策がなく、私も困っています。画像の再コンバートとか、画像のファイルヘッダを確認するのも。コメントとかカラーパレットとかで、決定的なものがありません。

が、ちょっと作ってみたのが、

1.空の画像を作って

2.ユーザのアップロードしてきた画像を空の画像にコピーする

これだといけそうな気がしたので作っていました。

[PHPで画像XSSの一味変わった対策より引用]

えーっと、さっそく試してみましたが、意図通りに動いていないようです。パレットにJavaScriptを仕込んだ画像を食わせると、出力された画像にもJavaScriptが残っていました。

JavaScriptを埋め込んだ画像を作ってみました」で紹介した画像だとJavaScriptが消えるのですが、これはJavaScriptを仕込んだパレットが実際には利用されていない色だったからのようで、そのパレットの色も使うようにすると、JavaScriptがそのまま残りました。下の画像がテストに使ったものです*1

画像の説明

また、フルカラーのBMPの画像本体にJavaScriptを仕込んだ場合も、そのまま出力されました。この方式では、フルカラーBMPのイメージ本体に仕込まれたJavaScriptは、原理的に除去されないように思います。

画像の説明

以上、ご報告まで。がんばってください。

*1 実際には、はてブのはせがわさんの指摘のように、MS07-057を適用したIEの場合、JavaScriptが動くのはBMPのみですが

本日のツッコミ(全2件) [ツッコミを入れる]

_ はせがわ [徳丸さんはわかってらっしゃると思いますが、念のために整理してフォローしておきますと、MS07-054適用以前はBMP..]

_ 徳丸浩 [はせがわさん、フォローありがとうございます。えーっと、MS07-054ではなくてMS07-057ですよね(MS07-..]


2007年12月05日 ツッコミSPAM対策奮戦記

_ツッコミSPAMフィルタを1バイトバージョンアップした

はてなダイアリーの方で、ツッコミSPAMとして以前報告したが、www.seozone.jpからのツッコミSPAMが激しかったので、tdiaryのフィルタを追加して対応していた。
コードを公開しよう。

if /www\.seozone\.jp/ =~ comment.body then
    return false

単純明快、コメント本体にwww.seozone.jpが含まれていたら、ツッコミを拒否していたのだ(^^;
単純・素朴なものだったが、11/26以降、突っ込みSPAMの被害はやんでいた。 ところが、本日続けて7件の突っ込みSPAMが送信されていた。みると、奴は進化していたのだ。http://wwww.Seozone.jp/というように、1バイトを大文字にするだけで、上記のフィルタを潜り抜けてきた。
しまった、正規表現にiオプションをつけておくのだった(^^;

if /www\.seozone\.jp/i =~ comment.body then
    return false

これで当面は大丈夫かと。
ツッコミ/トラックバックSPAMとの戦いはまだまだ続く。


2007年11月26日 今日の用語特別版

_ DNS Rebinding

楽天テクノロジーカンファレンス2007にて、カーネギーメロン大学日本校武田圭史先生の講演を聴講して、DNS Rebindingの説明がとても分かりやすかったので、ここに再現を試みる(文責は徳丸にある)。

DNS Rebindingとは

DNS Rebindingは、DNSの返すIPアドレスを巧妙に変化させることにより、JavaScriptやJavaアプレットなどのsame origin policyを破り、インターネットからローカルネットワーク(通常外部からはアクセスできない)などに対してアクセスする手法をいう。

攻撃に必要なもの

攻撃者は、自分のコントロール可能なドメイン名(以下の例ではtokumaru.orgを用いる)を持っているおり、かつそのドメイン名のWebサーバー(以下、罠サーバーと呼ぶ)があること。

攻撃のシナリオ

DNS Rebindingは受動攻撃の一種であり、攻撃者は、攻撃対象にアクセス可能なユーザに対して、メールなどにより罠サーバー(ここでは、tokumaru.org)に誘導する。罠サーバーに誘導された後の流れは以下の通り。

画像の説明
(1)↑tokumaru.org のIPアドレスをDNSに要求
(2)↓210.188.204.136 (TTL=5秒)を返す
(3)↑HTMLコンテンツを要求
(4)↓HTMLコンテンツを返す
(5) 10秒後に、HTML中に埋められたJavaScriptがtokumaru.orgのコンテンツを要求(same origin policyには違反しない)
(6)↑すでにDNSのキャッシュは無効なので、再びtokumaru.org のIPアドレスをDNSに要求
(7)↓今度はプライベートアドレス 172.21.21.11(TTL=1日)を返す
(8)←JavaScriptは、ローカルネットワーク内のWebサーバー(172.21.21.11)にコンテンツを要求する
(9)→ローカルサーバー上のコンテンツが返る
(10)↑form.submit()などを用いて、(9)の情報を罠サーバー(210.188.204.136)に返す

上記のように、DNSのTTLを極端に短く(上記例では5秒)設定することにより、同一ドメイン上のコンテンツを要求する際にDNSサーバーへの問い合わせを再度実行させ、二番目の問い合わせに対しては、攻撃対象(外部からはアクセスできない)のIPアドレスを返すことがミソである。これにより、見かけ上はsame orign policyに抵触することなく、特定のユーザを中継することで、ローカルネットワークなどの資源にアクセスすることができる。

DNS Rebindingについては、金床氏の研究が有名で、Black Hat Japan 2007にて、金床氏の研究が発表された。これによると、JavaアプレットやFLASHのSocketを利用することにより、任意のプロトコルに対しても、ローカルネットワークに対する情報の収集や攻撃などが可能である。 金床氏による、DNS Rebindingのデモページはこちら

対策(DNS Pinningなど)は後で。


tokumaru.orgは単なる例なのに、徳丸浩までなんか悪い奴に見えますね。しまった。

自作コンパイラの部屋 > 用語集 > DNS Rebinding

追記(2010/03/01)

携帯電話の「かんたんログイン」に対するDNS Rebindingによるなりすまし脆弱性については「iモードIDを用いた「かんたんログイン」のDNS Rebinding脆弱性」を参照されたい。また、現実に発見された脆弱性については「ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された」で報告している。

本日のツッコミ(全7件) [ツッコミを入れる]

Before...

_ 徳丸浩 [(10)の中で、罠サーバーにIPアドレスを付記しました。]

_ yamagata21 [お手数をおかけしました~。ありがとうございます♪]

_ Hash [> (7)↓今度はプライベートアドレス 172.21.21.11(TTL=1日)を返す とありますが, 長い時間を指..]


2007年09月24日 変数に型のない言語におけるSQLインジェクション対策に対する考察(5)

_数値項目に対するSQLインジェクション対策のまとめ

 一連の議論では、以下の条件におけるSQLインジェクション対策について議論している。

  • SQLインジェクション対策において、バインド機構が利用できない(したくない)
  • 変数に型のない言語(Perl、PHP、Rubyなど)を使用している
  • 数値型の列の場合

この場合の対策としては、以下の二種類が機能する。

  • SQL文組み立ての前に、数値としての妥当性検証を行う
  • 数値項目もシングルクォートで囲み(クォートし)、文字列リテラルと同様のエスケープを行う

数値項目もクォートする方法

このうち、後者の積極的な推進者として大垣靖男氏がおられる。例えば、以下のような記事

すべての変数をエスケープする対策

この方法はすべてのデータベースに利用できる対策です。文字列,整数などデータ型に関わらず変数すべてを文字列としてエスケープすることにより,SQLインジェクションを100%防ぐことが可能となります。例えば,PostgreSQLのSQL文を生成する場合,以下のようにすべてのパラメータを文字列して処理・生成します。

$sql = "UPDATE user SET name = '".pg_escape_string($_POST['name'])."', age = '".pg_escape_string($_POST['age'])."' WHERE id = '".pg_escape_string($_SESSION['USERID'])."';";

[なぜPHPアプリにセキュリティホールが多いのか?:第5回 まだまだ残っているSQLインジェクション(2ページ目)より引用]

上記の「"', age = '".pg_escape_string($_POST['age'])."' WHERE id = '"」の部分がそれである。age欄が「24」の場合、SQLの関連部分は以下のようになる。

UPDATE user SET name = 'ほげほげ', age = '24' where 【後略】

一方、age欄が「';DROP TABLE user--」の場合は、

UPDATE user SET name = 'ほげほげ', age = ''';DROP TABLE user--' where 【後略】

このようにこのSQLはageという数値項目に、数値でない文字列をセットしようとしているので、実行時にエラーとなる*1が、SQLインジェクションは回避される。

SQLにおける暗黙の型変換の是非

数値をクォートして文字列にすると、SQL実行時には、数値型の列と文字列リテラルとの間に「暗黙の型変換」が発生する。先の例では、age列が整数型だとして、set ... age = '24' ...という式の実行時に、文字列('24')から、数値(24)への暗黙の型変換が発生する。

一般に、プログラミングの分野では「暗黙の型変換」が有害であると言われる場合があるが、SQLの場合はどうだろうか。たとえば、Oracle Technology Network(OTN)には、以下のようなドキュメントがある。

暗黙的なデータ変換と明示的なデータ変換

次の理由から、暗黙的な変換または自動変換ではなく、明示的な変換を指定することをお薦めします。

  • 明示的なデータ型変換ファンクションを使用すると、SQL文がわかりやすくなります。
  • 暗黙的なデータ型変換(特に列値のデータ型が定数に変換される場合)は、パフォーマンスに悪影響を及ぼす可能性があります。
  • 暗黙的な変換はその変換が行われるコンテキストに依存し、どんな場合でも同様に機能するとはかぎりません。たとえば、日時値からVARCHAR2型へ値を暗黙的に変換すると、NLS_DATE_FORMATパラメータに指定されている値によっては、予期しない年が戻される場合があります。
  • 暗黙的な変換のアルゴリズムは、ソフトウェア・リリースやOracle製品の変更によって変更されることがあります。明示的な変換を指定しておくと、その動作は将来的にも確実になります。

[Oracle SQLの基本要素より引用]

このように、暗黙の型変換は、実装依存であるため動作が不確実になることや、性能面の不利などで推奨されていないことがわかる。

興味深いデータがある。ockeghem(徳丸浩)の日記 - 数値リテラルをシングルクォートで囲むことの是非を書いて以来、筆者のブログに「ORA-01722」の検索キーワードで訪れた人が累計74人もおられる。同じキーワードにより、Gooleなどで検索してみると、、同エラーコードに対する対処法の質問や、対処法FAQの説明などがいくつもヒットする。FAQができるくらい「暗黙の型変換」にはまる人が多いことになる。筆者のブログへの来場者も、現実に「ORA-01722」のエラーで困り果てた末にやってきて、がっかりして(なんだ、SQLインジェクションの説明か)立ち去った人が大半ではないだろうか。すなわち、現実に暗黙の型変換は、予期しない問題に遭遇する可能性が高く、書法的に推奨されない。

数値をクォートすると性能が悪化するケース

次に性能面ではどうか。筆者自身は、数値をクォートすることで性能劣化するパターンを見つけられていなかった*2が、最近奥 一穂のお仕事ブログに興味深いデータが示された。

鴨志田さんに教えていただいたのですが、MySQL のクエリは数値をクォートしない方が高速になるらしいです。たとえば以下の例では、160万件の整数から4の倍数を数えていますが、数値をクォートしないほうが約50%も高速になっています。

[Kazuho@Cybozu Labs: MySQL の高速化プチBKより引用]

筆者も手元のノートPCで追試を試みたところ、50%もの差はでなかったが、数値をクォートしない方が約20%高速になるという結果が確認できた*3

また、SQLインジェクション対策に限らず、SQLにおける暗黙の型変換という広い文脈でとらえると、既に報告の通り、インデックスが利用されなくなるために性能が大幅に低下する場合がある。

ではどうしたらよいか

今まで見てきたように、数値をクォートしてエスケープする方法は、数値項目に対するSQLインジェクション対策としては機能するが、副作用が多い。筆者としてはSQLを組み立てる直前での数値チェックをお勧めしたい。以下のような仕様の関数を作ればよいだろう。

  • 入力パラメータを数値として妥当性チェックする
  • 妥当の場合は数値をそのまま返す
  • 妥当でない場合は例外を発生させる

この関数は、pg_escape_stringなどと同じように使える*4ので、置き換えなどもしやすいであろう。先にあげた「暗黙の型変換」のデメリットが解消されるだけでなく、SQL実行前にエラー(例外)にできるので、アプリケーションの信頼性という点でも少し有利であろう。

なお、このエントリーはSQLインジェクション対策のための数値チェックであるが、アプリケーションの信頼性向上のための「入力値検証」は、上記とは独立に実施されることをおすすめする*5




自作コンパイラの部屋 > Web Application Security(WAS)に関する話題の目次 - SQLインジェクション > 数値項目に対するSQLインジェクション対策のまとめ

*1 ockeghem(徳丸浩)の日記 - 数値リテラルをシングルクォートで囲むことの是非参照

*2 SQLの「暗黙の型変換」で実行速度が遅くなるのはどのような場合か参照

*3 奥氏の環境はMyISAMだが、筆者の環境はInnoDBというエンジンの違い、奥氏はキー指定をしていないが、徳丸はキー指定をしているなどの違いに依存するものと思われる

*4 数値のクォートは外す必要がある

*5 アプリケーションの先頭で行う入力値検証は業務要件により行うべし参照


2007年09月15日 [JavaScript]ラベルつきbreak文

_ループやswitch以外のブロックを脱出するbreak

JavaScriptでは、breakにラベルを指定できて、通常は多重ループを抜ける場合に用いられるような説明がなされていると思う。しかし、ラベル付のbreakは、ループやswitch以外の文を終わらせることもできる(Javaもそう)。
 例えば以下のようなプログラム。

document.writeln("A");
L1: {
    document.writeln("B");
    L2: {
        document.writeln("C");
        break L1;   // L1の文を終わらせるので、D、Eは実行されない
        document.writeln("D");
    }
    document.writeln("E");
}
document.writeln("F");
【実行結果】
A
B
C
F

多重ループを一気に脱出する場合は、それなりに用途を思いつくが、ただの文ブロックを抜けるとなると、限りなくgotoを使っているような感覚を覚える。文ブロック以外に、try/catch/finallyやwith文、if/elseなどでも同様である。

あえて文ブロックをbreakで抜ける用途を強引に考えてみた。ループで配列をサーチするような場合に、対象が見つかった場合と見つからなかった場合で処理を分けたい。以下のように書いてみた。

function search(a, x) {
    L1: {
        for (var i = 0; i < a.length; i++) {
            if (a[i] == x) {
                // 見つかった場合の処理
                alert('見つかった:a[' + i + '] == ' + x);
                break L1;
            }
        }
        // 見つからなかった場合の処理
        alert('見つからなかった:' + x);
    }
    // 共通の後始末(あれば)
}
search([1, 2, 5, 6, 8], 5);    // 見つかる場合の呼び出し例
search([1, 2, 5, 6, 8], 7);    // 見つからない場合の呼び出し例

うわっ、これは駄目だ。こんなへたくそなプログラムは人様には見せられない・・・
 まぁ、こういう場合は、returnを使うんでしょうね。ただ、上記の例にはないが、「共通の後始末」が必要な場合は、ちょっとだけ上記のような書き方をする動機づけになる。try/finallyを使えばすむ話ではあるが。

JavaScript 第5版

追記(2007/09/16 13:20)

「へたくそな」例は僕だったらこう書くでしょうね。

function searchIndex(a, x) {
    for (var i = 0; i < a.length; i++)
        if (a[i] == x)
            return i;
    return -1;
}
function search(a, x) {
    var index = searchIndex(a, x);
    if (index >= 0) {
        // 見つかった場合の処理
        alert('見つかった:a[' + index + '] == ' + x);
    } else {
        // 見つからなかった場合の処理
        alert('見つからなかった:' + x);
    }
    // 共通の後始末(あれば)
}
search([1, 2, 5, 6, 8], 5);    // 見つかる場合の呼び出し例
search([1, 2, 5, 6, 8], 7);    // 見つからない場合の呼び出し例

あまり気の利いたプログラムではないと僕自身思いますが、こういう単純素朴な形なるように一生懸命考えるのが最近の好みです。そして、僕自身は多重ループからの脱出であっても、ラベルつきのbreakは使わないと思います。上記の例は、JavaScriptのトリビア的なネタということで。



自作コンパイラの部屋 > オブジェクト指向プログラム言語としてのJavaScript > ループやswitch以外のブロックを脱出するbreak

本日のツッコミ(全3件) [ツッコミを入れる]

_ おごちゃん [「深い階層の途中まで抜ける」ってのは、たまに必要になることがあるけれど、たいていは「どこかで設計ミスしてる」ことが多..]

_ 徳丸浩 [おごちゃん、おひさしぶり~。見ていてくれたんですね。僕も、おごちゃんのブログは見てますよ。 日経mix(懐かしい)で..]

_ おごちゃん [どもども^^; 最近は... つーか、MIX以後は「言語の仕様なんてどーでもいーじゃん」とか思ってプログラム書いて..]


2007年09月09日 入力値検証について(2)

_ アプリケーションの先頭で行う入力値検証は業務要件により行うべし

 前回のエントリ徳丸浩の日記 - そろそろ入力値検証に関して一言いっとくか - Webアプリケーション脆弱性対策としての入力値検証についての内容をもう少し突っ込んでみたい。
 このエントリは、幸いにも何人かの人たちの建設的なコメントをいただくことができた。それにより、私自身の考えを整理し、深めることができたように思う。

T.Teradaの日記 - 2007-09-08
"週"記(2007-09-07)

 今回は、入力値検証の中でも、特にアプリケーションの先頭で行う入力値検証として何をすべきかということに焦点を絞って議論を進めたいと思う。
 その前に、まずHTTPの復習から。

HTTPリクエストは申請書にたとえると理解しやすい

 この日記の読者の多くは先刻ご存知だと思うが、HTTPは非常にシンプルなプロトコルである。WebサーバはHTTPリクエストを受付け、HTTPレスポンスを返す。それを延々と繰り返す。HTTPリクエストには、ファイル名に加えて種々のパラメータをセットできる。このパラメータ類を「Webアプリケーションの入力」とここでは呼ぶことにしよう。具体的には、クエリ・ストリング、POSTパラメータ、Cookieなどである。

 HTTPリクエストを発行する際には、通常はHTMLフォーム上で規定された形式に従うが、現実にはどのようなパラメータをどのようにセットすることも呼び出し側の勝手である。ここで、HTTPリクエストを申請書にたとえてみると、申請書の形式は一応HTMLフォームで規定されている。しかし、申請書記入者は、指定の申請書形式に従わずに、項目を勝手に追加したり、申請書指定の形式(文字種や文字数など)を無視して自由に項目を記述することができる。

アプリケーションが申請書を受け付けるかどうかは早期に決定したい

 役所や銀行などで、申請書類を提出すると、まず窓口で、申請書が所定の書式に沿っているかをチェックされ、一箇所でも間違いがあると、つき返される。一方、書式に間違えがなければ、受理され、バックオフィスで処理されることになる。窓口での書式確認は、形式ばっているようにも見えるが、申請書の間違いにバックオフィス側で初めて気がついて、申請書が行ったり来たりする無駄を考えれば、一応の合理性がある。
 これと似たことはアプリケーションにもある。HTTPリクエストに記載されたパラメータ類は、アプリケーションの期待する形式になっているかどうかは保証されていない。仮に、アプリケーションの処理が相当進んでから、たとえば必須項目が足りずに処理が継続できなければ、今までの処理をロールバックして「なかったこと」にしなければならない。場合によっていは、ロールバックができずに、オペレータの手作業により、リカバリーしなければならないケースもあるだろう。現実には、上記は相当低レベルな話であり、アプリケーションは以下のような入力値チェックを「処理に先立って」実施することになる。

・必須項目が揃っているか
・各項目は指定の書式(型、文字数など)に従っているか
・形式が決まっている場合のチェック(電話番号、メールアドレスなど)

 これらのチェックが通れば、申請書は受理され、処理にまわることになる。さて、ここからが本題である。

 申請書に相当するHTTPリクエストが受理される要件は、セキュリティ上の都合ではないはずである。例えば、以下のような会話はどうか。

役人:えーっと、中村さん、住所欄に「'」は使えませんよ。書き直してください。
中村:そう言われても、アパート名がこうなんだからどうしようもないでしょ。
役人:とにかく駄目なものは駄目です。別の文字にしてください(*1)。

(*1)うわ、サニタイズ言うな

 すなわち、申請書が受理されるかどうかの条件は、原則として全て業務要件によって決まるはずである。通常、現実の申請書には、書式が詳しく説明してあり、その条件を満たしているにも関わらず返却されたら、申請者は相当ムカツクはずである。

ケーススタディ

 さて、T.Teradaの日記には以下のコメントがあるが、

徳丸さんの日記は、ユーザがテキストボックスなどで自由に値を入力できる住所などのデータを主に対象としたものだと思う。

 私は、ラジオボタン、チェックボックス、セレクトボックスなどの場合でも、業務要件として入力チェックをすべきだと思う。これは申請書のたとえで言えばこういうことである。
 たとえば、申請書の「性別欄」には、「女」や「男」と文字で記述する場合もあるが、多いのは男の場合は「1」、女の場合は「2}と番号が記載してあって、番号に丸をつけるか、番号を記入欄に記載する。申請書にはその方法が指定されている。ヤカマシい役所であれば、「『男』と書かずに、1に丸をつけてください」と言われるかもしれない。
 Webアプリケーションの場合で言えば、性別欄がラジオボタンになっていて、その値が男の場合は「M」、女の場合は「F」だとする。性別欄が「M」か「F」のいずれかでなければ処理を継続できないわけだから、この値チェックは業務要件として必要である。

 一方、先のエントリで問題になった、任意列でソートできるテーブルの場合はどうか。
 この場合、ユーザの入力は、ソート対象となる列指定である。列の指定方法はアプリケーションの実装として決めればよいわけで、列番号(1,2,3...)でも良いし、表の各項目の論理名であってもよいし、対応するDBの物理的な列名を採用することもできる(前回議論の対象になった方法はコレ)。私自身の好みでいえば、DBの列名を外部にさらすのは色々な意味で抵抗があるので、たとえば列番号を指定してもらって、アプリケーションの内部で列名に変換するようなロジックにするだろう。
 いずれにせよ、「申請書」に書くべき内容はソート対象の列指定であるが、その書式はアプリケーションの要求として決まっているものであって、一種の業務要件としてチェックすべきだということである。列番号を指定するのであれば、整数であることや数値の範囲のチェックが必要であろうし、列の論理名あるいは物理名を指定するのであれば、名前のチェック(文字種ではなく、列名として存在するか)が必要である。これらはいずれもSQLインジェクション対策としては、そこまでやる必要はない。あくまで、アプリケーションを正常に動作させるために必要な条件である。

「システム起源のデータ」をどうするか

 T.Teradaの日記には、Webアプリケーションには「システム起源のデータ」というものがあって、この場合は入力値検証をすべきであると指摘されている。現実のWebアプリケーションに「システム起源のデータ」が存在することを私も認めるものであるが、しかし、HTTPリクエストが必ずしもアプリケーションに制約されないという条件下では、「システム起源のデータ」をHTTPにより受け渡しすることは非常に危険だ。私は「システム起源のデータ」も(広い意味で)ユーザからの入力に限定すべきであると考える。先に述べたソート対象列の指定のその一例である。そして、ユーザからの入力であれば業務要件としてのチェックが可能であるが、そうでないもの(セッションIDなど)は、極力少なくするべきであると思う。この点についてはまた稿をあらためて説明したい。


まとめ

 アプリケーションの先頭で行う「入力値検証」は、従来セキュアコーディングの文脈で語られることの多かったが、現実には業務要件として行うべきものである。そして、業務要件として受付可能なリクエストは、特別なことがない限り処理されるべきであり、逆に処理できないものは業務要件として、入力に制限が掛っているべきである。例えば、メールアドレスに改行が混ざってはいけないというのも、申請書(伝票)の書式として要求できるものであり、現実問題、改行の入ったメールアドレスには(セキュリティ以前に)処理できない。
 さて、業務要件としての入力値検証を実施すれば、結果としてクロスサイト・スクリプティング(XSS)対策やSQLインジェクション対策にもなっているケースは多いが、これらインジェクション系脆弱性対策と、アプリケーションの先頭での入力値検証は別物として考えた方がよいと思う。アプリケーションにはバグはつきものであり、入力値検証が正しく実装されていない場合もある。また、アプリケーションの脆弱性検査や保守をする際に、入力値検査で「検査済み」の項目と、それ以外の項目(入力値でないものも含む)を明確に区別することが難しいことによる。入力値検証が業務要件チェックとして行われている以上、脆弱性対策はそれら値を使う時(出力時)にすべきものとした方が、考え方がシンプルになり、アプリケーションの開発ライフサイクルのトータルで考えれば結局は安上がりになると考える。


2007年09月05日 そろそろ入力値検証に関して一言いっとくか

_ Webアプリケーション脆弱性対策としての入力値検証について

 Webアプリケーションのセキュリティ対策としての「入力値検証」について色々言われている。セキュアコーディングの基本は入力値検証だといわれたり、さほど重要でないと言われたりしている。本当のところはどうなのだろうか。以下、バイナリデータを扱う場合の多いミドルウェア(Webサーバーなど)と対比しながら、この問題を掘り下げたい。

バイナリデータの場合(≒ミドルウェアの場合)

 バイナリデータでは、入力検証が重要である。少し前にmod_imagefightを取り上げた(画像版サニタイズ言うな(2))ので、ビットマップ画像を例に説明しよう。
 その際に使用したBMP形式の説明を再掲する。
0000:MARK(2) ='BM'
0002:ファイルサイズ(4)             *
0006:予約1(2) =0
0008:予約2(2) =0
000A:ビットマップ開始位置(4)       *(間接的にサイズに関連)
000E:ヘッダサイズ(4) =0x28         *
0012:イメージ横幅(4)               *
0016:イメージ高さ(4)               *
001A:プレーン数(2) =1
001C:ピクセルあたりのビット数(2) 1,4,8,24  *
001E:圧縮形式(4) =0,1
0022:圧縮後のイメージサイズ        *
0026:水平解像度(4)                 *(間接的にサイズに関連)
002A:垂直解像度(4)                 *(間接的にサイズに関連)
002E:使用色数(4)                   *
0032:重要な色数(4)
0036:カラーパレット
ZZZZ:ビットマップ
 ヘッダの中には、サイズに関連する値が多いことに注意されたい。上手上図において、サイズに関連する(可能性のある)ものを「*」により示した。これらの値を検証することは重要である。
 例えば、実際に受け取ったファイルのサイズと、BMPヘッダ中の「ファイルサイズ欄」が矛盾したら処理が継続できない。また、イメージの横幅と高さを鵜呑みにしてメモリを確保していたら、これらに巨大な値を入れるだけで(画像の中身は送らなくても)、アプリケーションは巨大なメモリをアロケートしようと試みるので、簡単にDoS攻撃が成立してしまう。また「ピクセルあたりビット数」が「1,4,8,24」のいずれかになっていることもチェックする必要があるだろう。
 BMPの例に限らず、一般にバイナリデータの形式は、「データ長」に続いて「データ本体」が続く場合が多い。このデータ長に間違ったあるいは巨大なデータが入っていることをチェックしなければ脆弱性の原因となる。「入力データのチェック」が必要となる所以である。
 すなわち、バイナリ形式のデータを扱う上では、

・データの整合性を保証することにより動作を確実にする
・リソース割り当てを適当に制限してDoS耐性を持たせる

などのために、入力の検証は必須なのである。
 また、ここで注目して欲しいことは、BMPの画像イメージの内容まではチェックしないことだ(圧縮されている場合は別)。
 すなわち、入力のチェックといっても、データの中身をチェックするのではなく、いわばデータの構造や枠組みをチェックしていることになる。そりゃそうだろう。TCP/IPでやりとりできるデータに制限があったり(例えば「'」があればエラーになる)、勝手に書き換えられたり(たとえば「;」が「_」に置換される)、勝手に削除される(例えば「<」や「>」があれば削除される)としたら、TCP/IPは使い物にならなくなる。

Webアプリケーションの場合

 Webアプリケーションの場合で考えると、入力データの構造のチェックとしては以下が考えられる。

・HTTPヘッダ(Cookieなど)の形式チェック
・クエリ・ストリングの形式チェック
・POSTデータの形式チェック(application/x-www-form-urlencodedなどの)

 これらは確かに必要だろう。しかし、これらチェックはアプリケーションの役目というよりは、ミドルウェアやライブラリの役目だ。ミドルウェア類からアプリケーションに渡されるデータは、データの中身であって、上記の形式チェックや構文解析は既に終わっている。
 ミドルウェア側の構文解析などがすんでしまえば、入力データがいかなる値であっても、データを受け取った時点で直ちにセキュリティ上問題が起こるわけではない。問題が発生するのは、その値を使う時である(当たり前だ)。
 その例として、クロスサイト・スクリプティング(XSS)やSQLインジェクションなどのインジェクション系脆弱性があり、値を使う時に(出力時に)エスケープなどを行う必要があるのだ。データを使う時にどの文字が問題になるかは、データの使い方(HTML、SQL、・・・)によって変わる。そのため、データを使う直前まで、エスケープなどの処理はできないことになる。

アプリケーションの入力チェックですべきこと


 Webアプリケーションでも入力値のチェックはするべきであるが、それはセキュア・コーディングという意味においてではない。ユーザが間違ったデータを入力した場合にできるだけ早期に誤りを発見して、アプリケーションの信頼性を高めるという、ビジネス要件の意味合いからである。したがって、電話番号とか、価格とか、メールアドレスのように文字種や桁数などが限定される場合には、ぜひその形式になっているかをチェックすべきである。
 しかし、住所欄や掲示板への投稿本文欄など、実質的に文字種を制限できない場合も多い。そのような場合には、入力チェックとしてできることは、せいぜい桁数(文字数)のチェックくらいである。
 すなわち、そもそも入力チェック(文字種チェック)は出来る場合と出来ない場合があり、文字種チェックなどが脆弱性対策に(結果として)役立つ場合もあれば、役立たない場合もある。したがって、Webアプリケーションの場合、入力チェックは根本的な脆弱性対策にはなりえず、「やっておいた方がよい」という保険的な対応にすぎない。

まとめ

 古典的なセキュアコーディングの原則の一つに入力値検証があることから、Webアプリケーションにおいても入力値検証が脆弱性対策の基本であるかのような説明を見かけることが多い。しかしバイナリデータを扱うミドルウェア等の場合、入力データの検証とはデータの枠組みの検査であって、入力データそのものの検査ではない場合が多い。一方、Webアプリケーションにおいて、入力値の検査は入力データそのものの検査を論じる場合が多い。ここにギャップがある。
 (Web)アプリケーションの場合、通常データの枠組みは解析済みであり、データそのものを受け取る。したがって、ミドルウェアなどの場合に比べて入力値検証の重要性は低い。一方、アプリケーションの場合、ビジネス要件に従った入力値検証は重要である。下表にこれらをまとめた。

ミドルウェアアプリケーション
データの枠組みの検査重要重要でない
データそのものの検査不要な場合が多い重要(*1)
(*1) セキュリティの要請ではなく業務要件として

 このように整理すると分かりやすい(上記は大まかな議論であって、もちろん例外はありえる)。
 従来混乱しがちだった「Webアプリケーションのセキュリティ対策における入力値検証」の意味を考える出発点として、新しい視点を提案するものである。

本日のツッコミ(全12件) [ツッコミを入れる]

Before...

_ 別の通りすがり [カラム名なら「"」で括るのが正しいですよ。 カラム名に予約語があったら困るので当然そういう文法が言語に用意されている..]

_ masa__ [別の通りすがりさん > カラム名なら「"」で括るのが正しいですよ。 恥ずかしながら知りませんでした、ありがとうござい..]

_ masa__ [フレームを発生させてしまい申し訳ありません。 カラム名のクォートについて調査した結果だけ報告して最後とさせて頂きます..]



[PR]小規模ECサイトに最適なWAF、SiteGuard Lite

ockeghem(徳丸浩)の日記はこちら
HASHコンサルティング株式会社

最近の記事

最近のツッコミ

  1. Hash (11-18)
  2. Nkzn (05-27)
  3. momo (05-06)
Google