第5章 セキュアVBScript/ASPプログラミング
[5-1.]
Requestへのアクセスは慎重に
InternetInfomationServer(IIS)のActiveServerPages(ASP)には,送られてきたHTTPリクエストの各種情報にアクセスできるRequestという強力な組み込みオブジェクトが用意されている。しかし,注意して使用しないと思わぬセキュリティ脆弱性を生んでしまうことがある。



 PDF
● ● ●
Requestオブジェクト
Requestオブジェクトは,IISのASPに標準で用意されている組み込みオブジェクトの1つであり,その名の通り,HTTPリクエストに関する各種情報へASPプログラムがアクセスするためのものである。このオブジェクトからはクエリストリング(URLの「?」以降のパラメタ部分)の項目,フォームのデータ項目,クッキーなどのさまざまな情報を取り出せる。例えば,クエリストリングのUserNameという項目の値を取得するは次のようにする。
    name = Request.QueryString("UserName")
また,SearchOptionという名前をもつクッキーの値を取得するためには次のようにすればよい。
    option = Request.Cookies("SearchOption")
5つのコレクション
WWWブラウザから送られてきたHTTPリクエストにかかわる各種の情報は,Requestオブジェクトの中の次に示す5つのコレクションによって保持されている。
 
QueryString(クエリストリング)
    QueryStringは,HTTPリクエストのクエリストリング(URLの「?」以降のパラメタ部分)に「名前=値」の形で記述された各項目の値を保持するコレクションである。
     
    QueryStringコレクションからの値の取り出しは,たとえば次のような式で行う。
      value = Request.QueryString("choice")
    これは,
      http://www.domain/foo.asp?choice=3&option=B
    といった形でURL上に指定されたchoiceというパラメタの値を取り出す例である。
     
    QueryStringから取り出せるデータ項目には,ユーザが辿ったハイパーリンク(<A>タグ)に埋め込まれたパラメタ項目だけでなく,GETメソッドで送られてきたフォームデータ項目も含まれる。
Form(フォーム)
    Formはフォームデータの各項目を保持するコレクションである。ただし,Formコレクションが保持するのはPOSTメソッドで送られてきたフォームデータ項目に限られる。GETメソッドで送られてきたフォームデータ項目はQueryStringコレクションが保持している。
     
    Formコレクションからの値の取り出しは,たとえば次のような式で行う。
      name = Request.Form("LastName")
    これは「LastName」という名前をがつけられたフォームデータ項目の値を取り出す例である。
Cookies(クッキー)
    Cookiesは,WWWブラウザが送ってきたクッキーを保持するコレクションである。
     
    クッキーは,あらかじめWWWサーバがWWWブラウザに預けておくと,HTTPリクエストのたびに毎回WWWブラウザがWWWサーバへ送り続けるようになる合言葉のようなデータであり,一連の複数WWWページからなる会話処理の連続性(セッション)を維持するために用いられるものである。クッキーは,「名前=値」の形のデータに有効期間やWWWサーバとの関連付けなどの属性が伴ったものであるが,Cookiesコレクションを通じて取り出すことができるのは「名前=値」のデータの部分だけである。
     
    Cookiesコレクションが保持しているデータの取り出しは,たとえば次のような式で行う。
      cart = Request.Cookies("ShoppingCartNumber")
    これは「ShoppingCartNumber」という名前をもったクッキーの値を取り出す例である。クッキーについては,1つの項目の値をさらに細分化して,
      名前 = 値 & 名前 = 値 &...
    といった項目リストとして取り扱う方法も用意されているが,その詳細についてはここでは割愛する。
ClientCertificate(クライアント電子証明書)
    ClientCertificateはWWWブラウザから送られてきたクライアント電子証明書の各記載項目を保持するコレクションである。クライアント電子証明書は,SSL通信の中でWWWサーバとWWWブラウザの両者が電子証明書を取り交わして相手が偽者でないことを相互に確認し合う際に使われるものである。
     
    WWWブラウザから送られてきたクライアント電子証明書にはユーザの身元を示すいくつかの情報が書かれており,たとえば次のような式でその値を取り出して利用することができる。
      email = Request.ClientCertificate("SubjectEMAIL")
    これは電子メールアドレスを取り出す例である。
ServerVariables(Webサーバの環境変数)
    ServerVariablesコレクションは,通常のCGIプログラムが環境変数を通じて参照できるのと同等のパラメタへのアクセスを提供している。たとえば,次のような式でパラメタが参照できる。
      page = Request.ServerVariables("HTTP_REFERER")
    これはHTTPリクエストに含まれているReferer:ヘッダの内容を取り出す例である。
コレクションの省略
ASPには次のような,コレクション名を書かず直接Requestオブジェクトに対して項目の値の取り出しを要求する方法が用意されている。
    value=Request("price")
このようにして値を取り出す対象のコレクションを明示しなかった場合,Requestオブジェクトは次の順序で5つのコレクションを探索し,該当する名前をもつ項目が最初に見つかったところからその値を取り出してくる。
  1. QueryString
  2. Form
  3. Cookies
  4. ClientCertificate
  5. ServerVariables
Formコレクションを明示しないとどうなるか
画面1をご覧いただきたい。1つのテキストボックスと,これをPOSTメソッドで送信するサブミットボタンが配置されたフォームのシンプルなページである。このページのHTMLコードをリスト1に示す。テキストボックスには「msg」という名前がつけられている。
 
このフォームのデータを受け取って処理するASPプログラムをリスト2のように書いてみた。ここでは,Formコレクションを明示せずに「msg」という項目の値をRequestオブジェクトから取得し,WWWページに表示している(画面2)。
 
このプログラムの場合,URLのクエリストリングに次のような形でパラメタを指定して呼び出しても同様な表示が行われる(画面3)。
    responding.asp?msg=BoodBye
もともとPOSTされたフォームデータだけを処理することだけを目的としているのであるなら,こうしたコードを書くことは,プログラムの意図しない使われ方を暗に許してしまうことになる。
 
お気付きの読者もおられるだろう。明らかにこのリスト2のプログラムはクロスサイトスクリプティング脆弱性(=WWWページ上に第三者が悪意のスクリプトを埋め込むことが可能な問題)を有している上に,そのための悪用もいたって簡単である。
 
このプログラムはリスト3のように記述すべきであった。ここでは念を入れてHTTPリクエストのメソッドもPOSTであることを確認するようにしている。今度は意図せぬ呼び出しに応答してしまうことはなくなる(画面4)。
画面1 サンプルフォーム
1つのテキストボックスと,これをPOSTで送る1つのサブミットボタンの
シンプルなフォームがあるページである
 画面1 サンプルフォーム
リスト1 画面1のHTMLコード,form.htm
1 <HTML>
2 <BODY>
3    <FORM METHOD="post" ACTION="responding.asp">
4        <INPUT TYPE="text" NAME="msg"><BR>
5        <INPUT TYPE="submit">
6    </FORM>
7 </BODY>
8 </HTML>
リスト2 画面1からのポストを受け取るASPプログラム,responding.asp
RequestオブジェクトのFormコレクションを指定せずにWWWブラウザからのデータを受け取る仕組みとなっている
1 <HTML>
2 <BODY>
3    メッセージの内容は、
4    <% = Request("msg") %>
5 </BODY>
6 </HTML>
画面2 フォームを送信した結果
 画面2 フォームを送信した結果
画面3 クエリストリングからパラメタを与えた場合
Requestオブジェクトからの値の取り出しの際にFormコレクションが
明示されていないのでこのようなこともできる
 画面3 クエリストリングからパラメタを与えた場合
リスト3 リスト2のコードを修正
意図した以外の振る舞いをなるべく封じるよう,プログラムに工夫をしたところ
1 <HTML>                                  POSTメソッドのみ受け付けるようにする
2 <BODY>                                            
3 <% If Request.ServerVariables("HTTP_METHOD") <> "POST" Then %>
4    呼び出しのメソッドが間違っています
5 <% Else %>
6    メッセージの内容は、
7    <%= Server.HTMLEncode(Request.Form("msg")) %>
8 <% End If %>                       
9 </BODY>        ↑           間違いなくForm コレクションから値を取り出すようにする
10 </HTML>    HTMLEncodeでクロスサイトスクリプティング対策も施す
画面4 リスト3の実行結果
 画面4 リスト3の実行結果
ClientCertificateコレクションを明示しないとどうなるか
クライアント電子証明書の取り扱いの場面でRequestオブジェクトの省略記法を使用することはよもやないだろうとは思うのだが,もし本当に使用されると大きな問題になりかねないので,ClientCertificateコレクションを明示せずにRequestから値を取り出そうとしたときに起こる問題を紹介しよう。
 
クライアント電子証明書を用いたSSL通信の場合,暗号化されたセキュアな通信チャネルを確立する手続きの一環としてWWWサーバとWWWクライアントはそれぞれ自分の電子証明書を相手と取り交わす。ここでディジタル署名の仕組みを用いた検証が行われ,互いに通信相手が偽者でないことを確認し合うのである。IISにおいてもこの処理が自動で行われ,https:のURLに指定されたASPプログラムが呼び出された時点では,WWWクライアントの電子証明書の正当性はすでに確認済みの状態になっている。
 
クライアント電子証明書にはメールアドレスや所属組織などの情報が書き込まれているため,ユーザを認証したついでに,こうした記載項目をユーザのプロファイルにアクセスするキーやユーザに提供するサービスメニューの切替えのパラメタに利用すると入力の手間も省け,お互いに便利である。
 
リスト4に示すのは,HTTPリクエストに含まれていた検証済みクライアント電子証明書から電子メールアドレスを取得して表示するASPプログラムである(画面5)。
 
例によってここではClientCertificateコレクションを明示せずにRequestオブジェクトに直接値を要求している。そこで,次のようにクエリストリングに電子証明書と名前が重複するデータ項目(SubjectEMAIL)を設け,別人のメールアドレスを与えてやることができる。
    cc.asp?SubjectEMAIL=victim@domain
このことは,何らかの方法で入手したクライアント電子証明書があれば,任意の別人になりすますことができるという問題につながる。
 
すでに明らかなことではあるが,ClientCertificateコレクションの指定は決して省略すべきではないのである(リスト5,画面7)。
リスト4 クライアント電子証明書から電子メールアドレスを取り出すASPプログラム
RequestオブジェクトのClientCertificateコレクションを指定しないで値を取り出そうとしている
1 <HTML>
2 <BODY>
3    あなたの電子証明書のメール・アドレスは、
4    <% = Request("SubjectEMAIL")%>
5 </BODY>
6 </HTML>
画面5 実行例その1
クライアント電子証明書からメールアドレスを取り出したところ
 画面5 実行例その1
画面6 実行例その2
クエリストリングに同じ名前の項目を指定することで,クライアント電子証明書の
メールアドレスだとプログラムが思っているところへ別人のメールアドレスを滑り
込ませることができる
 画面6 実行例その2
リスト5 改善されたプログラム
1 <HTML>
2 <BODY>
3    あなたの電子証明書のメール・アドレスは、
4    <%= Request.ClientCertificate("SubjectEMAIL") %>
5 </BODY>                
6 </HTML>  ClientCertificate コレクションの指定は決して省略してはならない。
           念には念を入れる場合,万一に備えてここでもHTMLEncodeによる
           クロスサイトスクリプティング対策を行うことを推奨する。
画面7 改善後の実行例
同名のクエリストリング項目による電子メールアドレスのすりかえは成功しない
 画面7 改善後の実行例
リスト6 QueryStringとFormコレクションの2つを対象に項目を探索する関数
1 Function RequestQryForm(pName)
2    Dim str
3    str = Request.QueryString(pName)
4    If str = "" Then
5    str = Request.Form(pName)
6    End If
7    RequestQryForm = str
8 End Function
コレクションは必ず明示しよう
Requestオブジェクトからデータ項目を取り出すとき,コレクション名を明示しないことはある程度プログラムの柔軟性が増すことではあるが,プログラムの振る舞いのより多くの部分がWWWブラウザから送られてくる内容に依存するようになり,意図通りにプログラムを掌握できなくなるおそれがある。
クエリストリングとフォームの両立
アプリケーションプログラムによっては,WWWブラウザからのデータをクエリストリングからもPOSTされたフォームからも,どちらからでも受け取れるようにしたいといった要求がある。
 
このような場面でも,Requestオブジェクトのコレクション名を省略することは思いとどまっていただきたい。確率は高くないとはいえ,CookiesやServerVariablesなどと項目名が重複して問題を起こさないとは限らないからだ。
 
クエリストリングとフォームの両方からデータ項目を受け取れるようにしたい場合,たとえばリスト6のようにQueryStringコレクションとFormコレクションの2つだけを探索する関数を自作するなどして対処すべきである。
まとめ
ASPではRequestオブジェクトの5個のコレクションを通じてHTTPリクエストに関する情報を取り出すことができる。Requestオブジェクトに対してコレクション名を特定せずにデータ項目を要求する方法もあるが,それほど利便性が高まらないうえに,もたらされる害は無視できない。Requestオブジェクトのコレクション名は省略すべきではない。
関連記事
参考文献
『ASP デベロッパーズガイド』Andrew M.Fedorchek ,David K.Rensin 著,SE 編集部 訳,河端善博 監修,翔泳社
Internet Information Services オンラインマニュアル
(Windows 2000 Server などのソフトウェアに付属)