セキュアなWebアプリとは

1.誰にとっての「セキュア」か

プログラマー任せで良いのか

 ネットワークサービス全体がセキュアであることは、提供者側、利用者側の双方にとって当然に大事なので、あらゆる攻撃に対して、堅牢であることが望まれるわけですが、ネットワークセキュリティについての議論は豊富だけれども、Webアプリのセキュリティについての定式というか定番らしきものは、あまり目にしたことがありません。

 どうしてそうなのか、というと、ネットワークのセキュリティはネットワーク機器やサーバーの設定方法によるところが主なのに対し、Webアプリのセキュリティがプログラミング技法に依存していて、どちらかというと「プログラマー任せ」の傾向があるので、システム設計の段階でポリシーの議論になりにくいからなのかもしれません。

 あるいは、SEたちの間では、「セキュリティの問題はネットワーク側の問題」という雰囲気があって、自分たちの問題でないような感覚があるのかもしれません。

 特に、UNIX系から流れてきた「学術系かつネットワーク派」のSEは別として、汎用機系から流れてきた多くの「業務系かつホスト集中処理派」のSEには、もともとセキュリティに対する感性を育てる経験が乏しく、極めて危なっかしいWebアプリを作ってしまう可能性がある、と思います。

ネットワーク側とアプリ側の役割分担

 ネットワーク設計においては、セキュリティについて必ず「ポリシー(政策)」を定め、それに従ってすべてを設計していかなければなりません。アプリ主導型でネットワーク設計をしてしまうと、後で大変なことになってしまうことは明白です。

 これは、私の経験から強く感じていることですが、とにかく、ネットワークというのはすべてのシステムをその上に乗せる「共通基盤」であり、情報通信システムのインフラの中でも、最優先のものとして位置付けておかないと、たくさんのシステムが構築されてしまった後からでは、巻き返しがつかないものだと思います。

 けれども、ネットワークが万全を期して確保できる範囲は、アドレスやポートに対するパケットレベルや通信の暗稿化、ユーザー認証とネットワークOS間の信頼(ディレクトリサービス)ぐらいまでであって、いくらネットワーク側が万全でも、アプリが馬鹿な結果を吐き出してしまうと、それまでの苦労は水の泡になってしまいます。

 どう考えても、セキュリティに関してアプリにはアプリなりの役割と責任があるんだ、ということをもっと明確にしておかないといけない、と思います。

 例えば、サーバー管理までを含めたネットワーク側でいうと、正しく設定しているネットワーク環境に対して拒否型攻撃があった場合に、サーバーがダウンしたり乗っ取られてしまう前に、サーバーは自ら防衛行動を開始しなければならない、そういう責任でいるのに、利用者によるただの一回の操作で、アプリが出してはいけないデータを平気で送り出してしまうとしたら、そんな無責任なアプリの存在は、決して許されません。

セキュアを組織全体でお客様に提供する

 つまり、絶対に避けなければならない状況というのは、アプリ開発に携わるSEに対して、「自分たちは安全なネットワークに守られている」という「殿様気分」を持たせないことだと思います。そうではなくて、「自分たちもいかにしてセキュアをお客様に提供できるのか」ということを徹底して教育しないといけない、そう思います。

 私は、情報通信関係の仕事に携わっているのに、自分自身は電子決済に関するインターネットサービスは、あまり利用したくありません。恐らく大丈夫なんだろう、とは思うのですが、どこかしら不安が残っているからです。

 こうしたお客様側の「不安」を取り除くために、提供者側の組織全体が最大限の努力を払っているのか、それが一番大切なポイントだと思います。

2.見た目に安心なWebアプリ

まずはエラー処理から

 Webアプリの場合は、デバッグ環境が貧弱なので、誰しも結構自信のない状態でリリースしているのではないか、と思います。VCやVisual Basicで書いたCGIプログラムの場合は、プログラム上でエラーが生じても、サーバーのコンソールとイベントログに寂しいメッセージを出して黙ってしまうだけで(しかも、Windows2000では、メモリ管理が上達したので、IIS全体がこける可能性は減っているらしい)、Webアプリ利用者は「タイムアウト」という被害に遭うだけで済みます。

 けれども、ASPの場合はIISがいろいろと「不気味な」メッセージを吐いてしまうので、これは要注意なわけです。

 この「不気味な」メッセージは、利用者を不安のどん底に陥れるとともに、提供者側の信頼性を大きく損ね、場合によっては攻撃に対する甘さ、という評価にもつながるわけです。

 まずは、エラー処理をどのようにするのか、これがセキュアなWebアプリの第一歩であると思います。

とにかくエラーはトラップ

 開発段階で、プログラムエラーは除去していても、予期せぬデータに起因するエラーはどうしても発生してしまいます。そうした場合を想定し、多くの人は、目を瞑って次の「おまじない」をかけて祈っているはずです。

On Error Next Resume
'各処理
If Err.Number = 0 Then
    'データの更新など重要な処理
Else
    'エラーメッセージ
    Err.Clear
End If

 これはあくまでも「おまじない」であって、本心は「怖いものは見ないでおこう」という姿勢ですよね。実際に、利用者側から見て、どうしてエラーなんだ、ということが説得力をもって理解してもらうには、やはり可能な限りエラートラップをして、適切なエラーメッセージを返さなくてはなりません(あたりまえのことなんですが、これがなかなかできないのです)。

On Error Next Resume
'各処理
Select Case Err.Number
    Case 0
        'データの更新など重要な処理
    Case 1
        'これはこうだからだめですよ
        Err.Clear
    Case 2
        'これもこうだからだめですよ
        Err.Clear
    Case Else
        'ごめんなさい
        Err.Clear
End Select

 特に、インターネット上のWebアプリの場合は、イントラネットでの運用とは異なり、利用者の情報リテラシーの開きが大きいため、表示する文言を含めて配慮が必要です。

3.徹底的な偽装攻撃対策

新しい接続があった場合

 次に、実際の技法に入っていくわけですが、その前に、開発しようとしているWebアプリの構造を点検してみて、分岐やジャンプを含め、ページの一連の流れを見直してみます。

 最も注目すべき(最も危険な)タイミングは、当然ながら、利用者からデータを受け取る場面ですが、データの送信(フォームからのPOST又はURLを使ったQueryStringのGET)が行われるページに対して偽装攻撃されないようにするためにするにどうすれば良いか、ということを考えます。

 その際、最初に考えなければならないことは、アプリ利用者の一連のページ操作を制御し監視する方法についてです。まず第一番目に必要な制御として、新たに接続を確立した利用者に対しては、アプリの「トップページ」へ必ずルーティングするようにします。

 どういうことか具体的に言うと、データ送信など特定のページへの攻撃が突然行われても、「トップページ」から正しい順番に従って渡ってきた利用者でないと、データ送信のページを開けることができないようにすることを目的とします。

 技法としては、「global.asa」ファイルを利用し、Sessionオブジェクトのイベント動作を記述し、新たな接続があった場合(初めてサーバーと利用者のブラウザとの間にセッションが確立した時)に発生するイベントSession_OnStartでの動作を規定します。

<SCRIPT RUNAT=Server LANGUAGE=VBScript>
Sub Session_OnStart
    strStartPage = "/ThisVirtualDirectory/default.asp"
    strCurrentPage = Request.ServerVariables("SCRIPT_NAME")
    If StrComp(strCurrentPage, strStartPage, 1) Then
        Response.Redirect strStartPage
    End If
End Sub
</SCRIPT>

 なお、こうした「global.asa」による制御を貫徹させるためには、一つのアプリは一つの仮想ディレクトリに収まっている必要があります(一つの独立したアプリであるならば、決して仮想ルートのサブディレクトリに置いてはいけません)。

ページの順番はアプリが制御する

 さらに、「自分が呼び出される元になったページはあのファイルでないといけない」という書き方で、次々と、順番のページをそれぞれ制御していかなくてはなりません。

 この際に用いる技法としては、二種類が考えられます。

 一つには、CGIの環境変数「HTTP_REFERER」を参照し、正しい呼び出し元かどうかを確認する方法です。

'CGIの環境変数「HTTP_REFERER」には完全修飾名の値が入ります。
strPreviousPage = "http://"
strPreviousPage = strPreviousPage & Request.ServerVariables("SERVER_NAME")
strPreviousPage = strPreviousPage & "/ThisVirtualDirectory/default.asp"
If Request.ServerVariables("HTTP_REFERER") <> strPreviousPage Then
    Response.Redirect "./error.asp"
End If

 通常のCGIプログラミングでは上記の手法が用いられていると思いますが、環境変数「HTTP_REFERER」だけでは信頼性は高くないので、ASPではもう一つの方法として、Sessionオブジェクトを併せて用いることが望ましいと考えられます。

'呼び出し元のページでSession("PageName")が設定されていることを前提とします。
If Session("PageName") <> "TopPage" Then
    Response.Redirect "./error.asp"
End If
Session("PageName") = "UserCheck"

利用者の余計な操作を誘わない

 上記の制御ができていれば、例えば、URLに直打ちしたアドレスならば「HTTP_REFERER」は空になるので、偽装攻撃の防止はできるわけですが、それでも、URLを使ったQueryString渡しの場合、リンク先ページでブラウザのURL欄に標準出力の値がされてしまいますので、利用者にURLの値を変えてページを開き直してみたくなる誘惑を与えてしまいます。

http://ServerName/ThisVirtualDirectory/select.asp?ID=203

 ちょっとした配慮に過ぎないことではありますが、これを回避するには、面倒でも、いったんQueryStringで渡した値をSessionオブジェクトに入れて、さらに次のページにリダイレクトさせることで、「?ID=203」以降のURL表示をさせないようします。

If Request.QueryString("ID") <> "" Then
    Session("ID") = Request.QueryString("ID")
    Response.Redirect "./select-result.asp"
End If

4.その他の原則

適切なセッション管理

 セキュアの実現のためにSessionオブジェクトを使うことに言及しましたが、セッション管理そのものにも目を向けないといけません。例えば、入力項目の多いフォームで、利用者が全項目を入力するのに時間を要してしまう場合、あるいは、フォーム画面を開いたまま席をしばらくはずしてしまった場合など、セッションが切れてしまった場合のことを考えて、利用者に対してアプリは、そのような場合、どのようなことになるかを事前に通知していなければなりません。

 セッション管理を意識しなければならない場合として、ページ間のデータの渡し以外では、恐らく次のタイミングがあると思います。

(1) RDBMSとの接続を保持するタイミング

(2) アプリからのログオフするタイミング

(3) ログを吐き出すタイミング

 このうち、(1)については、別の稿で検討することとして、まず(2)について言えば、アプリ上でも固有のユーザー認証を行う場合は、アプリへのログオンと同様に、ログオフの概念を導入すべきです。そして、ログオフが済んだ後には、それまでのすべてのSessionオブジェクトのコレクションを破棄すべきです。

'ログオフ後に表示されるページ内
Session.Abandon

 さらに、(3)については、IISの標準のログ機能を使う場合、あるいは独自にログファイルを吐き出したり、専用のDBに吐き出す場合を問わず、最低限、(2)におけるログオンのタイミングとログオフのタイミングには、ログを記録すべきです。そうしなければ、IISの標準ログだけでは、後からの追跡調査で困ることになります。

適切なパスワード管理

 また、これは一般的原則ですが、アプリ上でのログオンにパスワードを使う場合は、パスワード誤入力の許容回数に制限をかけるべきです。一般的には、3回間違うと、そのアカウントを無効(永遠に無効、又はその当日のみ無効、など)にする、というのが多いようですが、利用者への利便性と扱うデータのセキュリティレベルをよく検討し、総合的に判断して設定すべきです。

 ネットワーク機器やサーバーのセキュリティと同様、アプリ側でも、こうしたロックアウトが発生した場合に、システム管理者グループに対して、警告メッセージをSendMailするようにしておく必要があります。

最後に

  Webアプリが共通のインターフェースとなりつつある今、私は、セキュリティの議論において、アプリ側の責任がもっと強調されなくてはいけないんだろうな、という思いが強まっていますが、そのベースには、冒頭にも書いたように、ネットワーク管理者がいつも不安に感じている(あるいは、感じるべき)その危機感に比して、一般的にアプリの開発者が呑気でいるケースが多いように思うからです。

 不正アクセスが生じたときに、真っ先に「ネットワークのセキュリティ」が問われる傾向があり、「アプリのセキュリティ」の弱さこそがセキュリティホールになっている場合でもネットワーク管理者の責任にされてしまう事態が最も心配です。そのサービスを提供すると組織が決定したので、ネットワーク側から適切にパケットをそのアプリに通したのに、とネットワーク管理者が主張しても、聞いてもらえないような雰囲気があるような気がしてなりません。

 ここに挙げた例では、定式や定番というところにまでは至りませんが、実践の中での教訓でも蓄積することが肝心と考え、まとめてみました。

 

最終更新日:2000年9月15日

>> システム開発のページへ >>戻る

Copyright© 1996-2000 Yumenokunisha All Rights Rserved