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

徳丸浩の日記


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インジェクション対策にもなっているケースは多いが、これらインジェクション系脆弱性対策と、アプリケーションの先頭での入力値検証は別物として考えた方がよいと思う。アプリケーションにはバグはつきものであり、入力値検証が正しく実装されていない場合もある。また、アプリケーションの脆弱性検査や保守をする際に、入力値検査で「検査済み」の項目と、それ以外の項目(入力値でないものも含む)を明確に区別することが難しいことによる。入力値検証が業務要件チェックとして行われている以上、脆弱性対策はそれら値を使う時(出力時)にすべきものとした方が、考え方がシンプルになり、アプリケーションの開発ライフサイクルのトータルで考えれば結局は安上がりになると考える。

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



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

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

最近の記事

最近のツッコミ

  1. おごちゃん (09-16)
  2. 徳丸浩 (09-16)
  3. おごちゃん (09-16)
Google