SPA で非同期通信を多用すると思います。その際に、非同期通信の場合の CSRF 対策について調査ました。
通常の同期処理(ページ遷移)の場合
ASP.NET MVC では CSRF 対策が超簡単です。
ViewのForm の中で Razor の @Html.AntiForgeryToken() を呼ぶだけでトークンを埋め込むことが出来ます。
トークンを検証するには Controller で ActionMethod の属性に ValidationAntiForgeryToken を指定するだけです。
@using (Html.BeginForm()){
@Html.AntiForgeryToken()
// 略
}
[ValidationAntiForgeryToken]
[HttpPost]
public ActionResult Create(ViewModel model){
// 略
}
ところが、非同期処理(Ajax)の場合は少々工夫が必要になります。
非同期処理(Ajax)の場合
Razor でクッキーのトークンとフォームのトークンを発行する関数を作成します。
作成した関数を呼び、hidden 値に設定し、POST 時にヘッダに付加してリクエストを送信します。
<script>
@functions{
public string GenerateRequestVerificationToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
</script>
<input type="hidden" id="requestValificationToken" value="@GenerateRequestValificationToken()" />
$.ajax({
url: "http://test/Create",
type: "POST",
dataType: "json",
data: {
data: hoge
},
headers: {
"RequestVerificationToken": $("#requestVerificationToken").val()
}
});
ヘッダに付与されたトークンを検証する関数を用意します。
private void ValidateAntiForgeryToken()
{
string cookieToken = "";
string formToken = "";
var tokenHeader = Request.Headers["RequestVerificationToken"];
if (!String.IsNullOrEmpty(tokenHeader))
{
string[] tokens = tokenHeader.Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
Controller の ActionMethod で先ほどの関数を呼びます。
public ActionResult Create(){
// 自前でヘッダに付与されたトークンを検証する
ValidateAntiForgeryToken();
// 略
}