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) |
このように整理すると分かりやすい(上記は大まかな議論であって、もちろん例外はありえる)。
従来混乱しがちだった「Webアプリケーションのセキュリティ対策における入力値検証」の意味を考える出発点として、新しい視点を提案するものである。
本日のTrackBacks(全3件)
[]
とてもよくまとまってると思います。では、現状はどうなのか、というのを過去〜最近の経験から。 「住所欄や掲示板への投稿本文欄など、実質的に文字種を制限できない」値に対してはエスケープするなど正しく実装されてることが多いです。それが要件として明記されている..
徳丸浩の日記 - そろそろ入力値検証に関して一言いっとくか - Webアプリケーション脆弱性対策としての入力値検証についてを表示したところ、以下のようになりました。 入力値検証の話題から、細菌対策の広告を出すとは・・・
XSS対策:JavaScriptのエスケープ(その3) - ockeghem(徳丸浩)の日記にて、JavaScriptのリテラルを動的生成する場合のエスケープ方法について検討したが、id:hoshikuzuさんから、考慮がもれているという指摘を受けた(2007-10-11 - hoshikuzu | star_dust の書斎 - JavaScript
例えば、バインドメカニズムがまだ広まっていなかった頃、数値型の SQLインジェクションを防ぐためには、入力値検証を行うのが一般的(*1)でしたから、“セキュリティの要請”として入力値検証が重要な面もあったかも知れませんね〜。<br><br>*1) 型変換も一般的でしたけどw
s/上手/上図/ ですね :-)
私も基本はエスケープという考え方を広めていきたいと思っています。ただし、少し複雑なアプリケーションを作ろうとすると、「例外」という問題が発生する可能性が非常に高いのでは?と感じることが多くなってきました。最も単純と思われるSQLInjectionの対策にしても、バインドメカニズムが使用出来ないケースは存在します。<br>エスケープだけでは防げないセキュリティの問題は存在していて、それらは入力値検証によって対策しなければならないという現実もあります。その部分のフォローが必要かなと感じる今日この頃です。<br><br>また、セキュリティの要請ではなく業務要件という考え方は、いまいち理解出来ません。例えば、パラメータの改ざんにより本来アクセス出来てはならないデータにアクセス出来てしまう問題があったとしたら、それは一般的なセキュリティの問題ではないということでしょうか?
>エスケープだけでは防げないセキュリティの問題は存在していて、<br>>それらは入力値検証によって対策しなければならない<br><br>そんなケースはないと思いますよ。<br><br>>パラメータの改ざんにより本来アクセス出来てはならないデータにアクセス出来てしまう問題<br><br>業務要件としてのセキュリティ要件であって、セキュアコーディングの話ではないですね。
突っ込みありがとうございます>みなさま<br>ミドルウェアとアプリケーションの役割は、すぱっと分かれるわけでもないので、ここで書いた話が絶対不変の真理などというつもりはありません。こう考えたら、おおむねうまく整理できるのではないかという提案です。<br>あと、入力値検証というのはあくまで入力値を受け取った時点での検証であって、その値を使う時にチェックしなければならないケースはあり得ると思います。例えば、リダイレクタに指定するURLに改行が入っていないかとかです。しかしこれとて、リダイレクタのAPI側で本来チェックすべきものだと思います。
>そんなケースはない・・・<br>お、そうなんですか。私が知らない間に、言語は進化しているようですね。<br>>業務要件としてのセキュリティ要件・・・<br>なるほど、セキュリティ要件を書く人は大変ですね。
masaさん<br>意見を述べるなら根拠を出して表明するようにしませんか。<br>「俺は知ってるけどお前ら知らないから言えるんじゃ」<br>と言われているようにしか聞こえません。<br>masaさんの方が間違っているかもしれないのに、<br>手の内を明かさないのでは誰も否定できません。
続けて。<br>masaさん曰く<br>「パラメータの改ざんにより本来アクセス出来てはならないデータにアクセス出来てしまう問題があったとしたら、それは一般的なセキュリティの問題ではないということでしょうか?」<br>意味がわからないです。「パラメータの改ざん」と言ったらなんでもそれに該当しますし、「本来アクセスできてはならないデータにアクセスできてしまう」と言ったら、セキュリティ上の脅威のCIAのCの全部がそれなのですから、何のことを指しておっしゃているのか特定できません。<br>そんな質問をしておきながら、「それは一般的なセキュリティの問題ではないということでしょうか?」なんて「そんなわけねーだろ」と言わんばかりの質問を投げかけるのは失礼です。
> 別の通りすがりさん<br>すいません、例をあげます。<br>SQLInjectionでしたら、<br>"select * from usertable order by " + columName + asc_or_desc;<br>といったコードでSQLInjectionの問題が発生した場合です。クォートされない個所への文字列挿入全般の問題なので、他にも例はたくさんあるかと思います。<br>このような問題にエスケープで対策出来ますか?分岐ロジックを書いていますか?実際は、入力値検証で対応するのではないですか?<br>と、いうことです。<br><br>パラメータの改ざん・・・のくだりは、アクセス認可と置き換えればよいでしょうか。<br>http://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/102.html<br>ここの図5の三段階目の入力値検証をイメージしています。
カラム名なら「"」で括るのが正しいですよ。<br>カラム名に予約語があったら困るので当然そういう文法が言語に用意されているわけです。http://www.postgresql.jp/document/pg653doc/j/user/syntax.htm<br><br>「asc_or_desc」のところは、そういうSQL文の作り方をしちゃいけないってだけです。入力のところでvalidationしなければならないってものではありません。<br><br>図5のものは、今ここで言われている validation のことではありませんね。脆弱性対策というより元々仕様で要求されているセキュリティ機能の一つでしょう。それが見えてはいけないことが仕様で定義されていなければこれが欠陥なのかどうか機械的判別はできないわけで、検査者の視点で言えば、検査者はそのとき「見えてはいけないもの」だということを常識で判断するのでしょう。それは仕様に欠陥があるという話で、ここで言われている脆弱性対策としての validation とは関係がないでしょう。
別の通りすがりさん<br>> カラム名なら「"」で括るのが正しいですよ。<br>恥ずかしながら知りませんでした、ありがとうございます。この指定については少し調査してみます。<br>asc_or_descの問題は「そういうSQL文の作り方をしちゃいけない」と言われると、「検証済ならよいのでは?」と思ってしまいます。ただ、入力値検証で対応しなければならない訳ではないですね。すいません。<br><br>>・・・それは仕様に欠陥があるという話で、・・・<br>常識で判断出来ることを仕様に書かなければならないとしたら、「セキュリティ要件を書く人は大変ですね。」という感想に変わりはありません。<br>私は、hidden値やselectboxで選択された値など、ユーザに悪意がない限り通常は改ざんされることがないデータが改ざんされ、その結果システムに悪影響が及ぶならば、業務要件がどうであれセキュリティの欠陥だと考えていますので。
フレームを発生させてしまい申し訳ありません。<br>カラム名のクォートについて調査した結果だけ報告して最後とさせて頂きます。<br>PostgreSQL8.2.4で検証したところ、「"」によって「"」がエスケープされますので、SQLInjection対策として有効であることが確認できました。テーブル名やカラム名に含まれる特殊文字のエスケープに(データベース名にも)適用が可能な、エスケープ関数が用意されている言語も多いようです。<br>http://www.go-pear.org/manual/ja/package.database.mdb2.intro-quote.php<br>http://msdn2.microsoft.com/ja-jp/library/system.data.common.dbcommandbuilder.quoteidentifier(VS.80).aspx<br>asc_or_descの部分は、「動的な入力値をそのまま使用して構文を組み立ててはいけない」と、理解することが出来ました。<br>不快な思いをさせてしまった方には深くお詫び致します。