atsukanrockのブログ

Microsoft系技術を中心にぼちぼち更新します

ASP.NETで忘れずにやっておくべきこと(1) ViewStateUserKeyを使う

はじめに

本エントリから何回かに分けて、ASP.NETアプリケーションを作成する際に、忘れずにやっておくべき(だと私が考える)ことを挙げる*1。第1回は「ViewStateUserKeyを使う」だ。

目的

ViewStateUserKeyを使うことで、手軽にワンクリック攻撃対策をする。ワンクリック攻撃については、『ASP.NET の組み込み機能を活用し、Web 攻撃を回避する』の「ViewStateUserKey」に解説あり。このリンク先には、ViewStateUserKeyを含む、ASP.NETのセキュリティ関連の組み込み機能について、良質な説明がある。ぜひご一読を。

注意

このページによるとGUIDを使うのが良いとのことだが、本エントリでは、MSDNで推奨されている、セッションIDを設定する方法を示す。セッションハイジャック対策を別途行うことで、このリンク先で指摘されている脆弱性を減らせるため。

実践

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    ViewStateUserKey = Session.SessionID;
}

これだけだ。この仕組を組み込みたい全てのページクラスの基底となる抽象クラスを作成し、そのクラスにこのコードを仕込む。当然ながら、その抽象クラスはSystem.Web.UI.Pageクラスの派生クラスだ。

このコードを仕込んだページで注意することは、ポストバックしている間(つまり、ビューステートが保存される間)、セッションIDが変わってしまわないようにすることだ。

これを守ることは簡単に思える。しかしながら、ASP.NETのセッションについて深い知識がなければ、思わぬところでこれを破ってしまう。例えば、HttpSessionState.SessionIDプロパティの意外な仕様だ。リンク先の「解説」をよく読むと書いてある。以下に引用する:

Cookie ベースのセッション状態を使用する場合、ASP.NET は Session オブジェクトが使用されるまでセッション データのストレージを割り当てません。その結果、セッション オブジェクトがアクセスされるまで、ページ要求ごとに新しいセッション ID が生成されます。アプリケーションがセッション全体に対する静的な ID を必要とする場合は、アプリケーションの Global.asax ファイル内に Session_Start メソッドを実装し、Session オブジェクト内にデータを格納してセッション ID を固定するか、アプリケーションの別の部分のコードを使用して、明示的に Session オブジェクトにデータを格納します。

簡単に言うと、セッションオブジェクトに値を格納するまでは、セッションIDがHTTP要求の度に変わってしまうということだ。私が試したところ、HttpSessionState.SessionIDプロパティ値の取得だけでは、これを防止できない。従って、前述のコードだけでは、セッションオブジェクトが空の場合に、前述の注意点を破ってしまう。そこで、次のようなコードも書く必要がある。

public class Global : HttpApplication
{
    protected void Session_Start(object sender, EventArgs e)
    {
        _session["__DummyValueToFixSessionID__"] = null;
    }
}

ここでのキー名は、何でも良いが、アプリケーションが使用するキー名を衝突しないようにする。GUIDを使うのも良いだろう。また、私が試したところ値はnullでも良いようなので、nullを設定すれば良いだろう。

おわりに

ViewStateUserKeyを使ったワンクリック攻撃対策に限れば、本エントリで示した方法だけで可能だ。しかしながら、後に記す予定のセッションハイジャック対策を施す場合に、前述の注意点を破ってしまう可能性がある*2。このように、ASP.NETの組み込み機能は、1つ1つは非常に便利なのだが、それらを組み合わせた際にうまく動作するようにするためには、それら1つ1つを熟知する必要がある。

*1:網羅的なリストではなく、私にとって当たり前でないものに限ることに注意

*2:その詳細については、セッションハイジャック対策について記す際に示すつもりだ