ASP.NET MVCでは、デフォルトでクライアントからクロスサイト・スクリプティングなどの攻撃の可能性があるデータが送信されていないかを検証する。その送信データにHTML文字列が含まれる場合、ASP.NET MVCはこれを「危険なデータ」として拒否するわけだ。例えば、第1回で紹介したHello/Postアクションを実行してみよう*4。
*4 もしweb.configでカスタム・エラー・ページを有効にしている場合には、あらかじめこれを無効化しておく必要がある。
「山田<script>」を入力して[送信]ボタンをクリック
これは、最低限のセキュリティ対策という意味で便利な機能ではあるが、時として、この機能が邪魔になることがある。
例えば、FreeTextBoxコントロールのように、入力に必ずタグ文字列が含まれるケースである。このようなページでは検証機能が有効になっていると、本来、受け付けるべき入力を受け付けることができなくなってしまう。
このような場合には、ValidateInput属性をFalseに設定し、ASP.NETによるデータ検証を無効にすればよい。
[AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
public ActionResult Post(String name) {
……中略……
}
<AcceptVerbs(HttpVerbs.Post)> _
<ValidateInput(False)> _
Function Post(ByVal name As String) As ActionResult
……中略……
End Function
この状態で、もう一度、Hello/Postアクションを実行してみよう。今度は、タグ文字列を含んだ値を送信しても、そのままアクションが実行されることが確認できるはずだ。
ただし、データ検証を無効にした場合、(当然のことながら)クライアントが「有害な」データを送信しても、ASP.NETはこれを検出することができない。原則として、ValidateInput属性をFalseに設定するのは、(アプリケーションやコントローラ全体に対してではなく)必要最小限のアクションに留めるのが好ましい。また、データ検証を無効にしたアクションについては、必ず自分で入力値の検証を行うべきである。
CSRF(Cross-Site Request Forgeries:クロスサイト・リクエスト・フォージェリ)とは、ひと言でいってしまうならば、外部サイトからのリクエストによって本来処理されるべきではない処理が行われてしまう攻撃のこと。CSRF攻撃を受けることで、例えば、掲示板や日記、コメント欄に勝手に書き込みが行われてしまったり、あるサービスから勝手に退会させられてしまったりといったような問題が起こり得る。CSRF攻撃については、「Security&Trust」フォーラムの記事『「ぼくはまちちゃん」 ――知られざるCSRF攻撃』が詳しいので、併せて参照いただくとよいだろう。
ここでは、取りあえずごく簡単なコードを書いて、CSRF攻撃の流れを理解してみよう。攻撃対象となるのは、第2回で登場したBook/Createアクションである。Book/Createアクションに対して、外部のサイトから「エンド・ユーザーに気付かれないように」データを登録してみよう。
<!--ページ・ロードのタイミングで
フォームの内容を自動的にサブミット-->
<body onload="document.fm.submit();">
<form name="fm"
action="http://localhost:4419/Book/Create" method="post">
<input type="hidden" name="isbn" value="978-4-7981-1957-X" />
<input type="hidden" name="title" value="Clacker Book" />
<input type="hidden" name="price" value="9999" />
<input type="hidden" name="publish" value="Clacker" />
<input type="hidden" name="published" value="2009-12-31" />
</form>
</body>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
親切そうな悪意あるページ
</title></head>
<body>
適当なメッセージ(By クラッカー)
<!--Submit.htmlを知らないうちに実行-->
<iframe src="Submit.html" width="1" height="1"></iframe>
</body>
</html>
何も知らないユーザーがリスト19のAttack.htmlにアクセスしたらどうだろう。Attack.htmlではSubmit.htmlを内部的に呼び出している。そして、Submit.htmlでは自動的にBook/Createアクションにポスト・データを送信しているのだ(1×1ピクセルのインライン・フレームであるので、普通のユーザーがSubmit.htmlの存在を感知する可能性は、まずない)。実際、Submit.html/Attack.htmlを任意のサーバにアップロードして、Attack.htmlにアクセスしてみれば、自動的に書籍情報が登録されてしまうことを確認できるはずだ。
今回、Book/Createアクションはログインの必要がないアクションであったが、これがログインの必要なアクションであっても同様である。Attack.htmlにアクセスしたユーザーがログイン状態にあれば、Attack.htmlはエンド・ユーザーのログイン済みの権限でもって、アクションを実行できてしまうのだ。
このように、ユーザーが意図しない状態でデータの操作が行えてしまうのは、当然のことながら大変困ったことだ。そこで、ASP.NET MVCではこうしたCSRF攻撃を防ぐためのValidateAntiForgeryToken属性とHtml.AntiForgeryTokenメソッドを用意している。
■ValidateAntiForgeryToken属性の使い方
やや前置きが長くなってしまったが、ValidateAntiForgeryToken属性とHtml.AntiForgeryTokenメソッドの、具体的な使い方を見てみよう。まずは、対象となるビュー・スクリプト(ここではCreate.aspx)にHtml.AntiForgeryTokenメソッドを埋め込む。
<%=Html.AntiForgeryToken() %>
<input type="submit" value="Create" />
Html.AntiForgeryTokenメソッドは、以下のような隠しフィールドを出力するとともに、「__RequestVerificationToken_Lw__」のような名前のクッキーを発行する。
<input name="__RequestVerificationToken" type="hidden" value="tJVYeJUMiD9s8HmKuzFESe3qHadxDC0HxD/wezi2kgK+cy/jVfzKJhEJy" />
これは入力フォームが正当なページであることをアクション・メソッドに知らせるためのキー文字列で、「ワンタイム・トークン」などと呼ばれる場合もある。
アクション・メソッドの側では、このトークンの存在をチェックして、入力元のページが妥当であるかどうかを判断するわけだ。当然、悪意あるページでは、この一時的に発行されたトークンを――しかも、隠しフィールドとクッキーと双方に隠されたトークンを推測することはできないはずである。
ビュー・スクリプトの準備ができてしまえば、後は防御対象となるアクションにValidateAntiForgeryToken属性を付与するだけだ。
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken()]
public ActionResult Create(Book b, FormCollection collection) {
……中略……
}
<AcceptVerbs(HttpVerbs.Post)> _
<ValidateAntiForgeryToken()> _
Function Create(ByVal b As Book, ByVal collection As FormCollection) As ActionResult
……中略……
End Function
トークンのチェックを行うために、アクションそのもののコードには一切手を加える必要はない。ValidateAntiForgeryToken属性がトークンの存在をチェックし、合致しないリクエストに対しては、以降のアクションをキャンセルするためだ。
以上、今回はアクション・メソッドに対して付随機能を追加するためのフィルタ属性について解説した。フィルタ属性を利用することで、アプリケーション本体のロジックには一切手を加えずに、認証やキャッシュ、例外捕捉のような定型的な機能を追加できることがお分かりいただけたことと思う。
さて、本連載もこの回で最終回となる。ASP.NET MVCには、ここで紹介したほかにもさまざまな機能が含まれているが、この連載で紹介した機能を十全に理解できていれば、おそらく基本的なアプリケーションは開発できるようになっているはずだ。この連載が、読者諸兄にとってASP.NET MVCを学習するうえでの足掛かりとなれば幸いである。
Copyright© Digital Advantage Corp. All Rights Reserved.