ASP.NET MVC アプリケーションでクロスサイト リクエスト フォージェリ (CSRF) 攻撃を防ぐ
クロスサイト リクエスト フォージェリ (CSRF) は、悪意のあるサイトが、ユーザーが現在ログインしている脆弱なサイトに要求を送信する攻撃です
CSRF 攻撃の例:
ユーザーが、フォーム認証を使用して
www.example.com
にログインします。サーバーはユーザーを認証します。 サーバーからの応答には、認証 Cookie が含まれています。
ログアウトしないまま、ユーザーが悪意のある Web サイトにアクセスします。 この悪意のあるサイトには、次の HTML フォームが含まれています。
<h1>You Are a Winner!</h1> <form action="http://example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click Me"/> </form>
悪意のあるサイトではなく、脆弱なサイトにフォームのアクションが POST されていることに注意してください。 これが CSRF の "クロスサイト" 部分です。
ユーザーが送信ボタンをクリックします。 ブラウザーには、要求を含む認証 Cookie が含まれています。
要求はユーザーの認証コンテキストを使用してサーバー上で実行され、認証されたユーザーが実行を許可されている任意のアクションを実行できます。
この例ではユーザーがフォーム ボタンをクリックする必要がありますが、悪意のあるページではフォームを自動的に送信するスクリプトを簡単に実行できます。 さらに、SSL を使用しても、悪意のあるサイトから "https://" 要求を送信できるため、CSRF 攻撃を防ぐことはできません。
通常、認証に Cookie を使用する Web サイトに対して CSRF 攻撃が仕掛けられる可能性があります。これは、ブラウザーが、関連するすべての Cookie を宛先 Web サイトに送信するためです。 ただし、CSRF 攻撃は、Cookie の悪用に限定されません。 たとえば、基本認証やダイジェスト認証も脆弱です。 ユーザーが基本認証またはダイジェスト認証でログインした後、 セッションが終了するまで、ブラウザーによって資格情報が自動的に送信されます。
偽造防止トークン
CSRF 攻撃を防ぐために、ASP.NET MVC は偽造防止トークン (要求検証トークンとも呼ばれます) を使用します。
- クライアントは、フォームを含む HTML ページを要求します。
- サーバーは、応答に 2 つのトークンを含めます。 1 つのトークンは Cookie として送信されます。 もう 1 つは非表示のフォーム フィールドに配置されます。 敵対者が値を推測できないように、トークンはランダムに生成されます。
- クライアントはフォームを送信するときに、両方のトークンをサーバーに送り返す必要があります。 クライアントは Cookie トークンを Cookie として送信し、フォーム データ内にフォーム トークンを送信します (ユーザーがフォームを送信すると、ブラウザー クライアントによって自動的に行われます)。
- 要求に両方のトークンが含まれていない場合、サーバーは要求を許可しません。
非表示のフォーム トークンを含む HTML フォームの例を次に示します。
<form action="/Home/Test" method="post">
<input name="__RequestVerificationToken" type="hidden"
value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />
<input type="submit" value="Submit" />
</form>
同じ配信元ポリシーが原因で、悪意のあるページがユーザーのトークンを読み取ることができないため、偽造防止トークンはうまく機能します (同じ配信元ポリシーを使用すると、2 つの異なるサイトでホストされているドキュメントが互いのコンテンツにアクセスできなくなります。そのため、前の例では、悪意のあるページが example.com に要求を送信できますが、応答を読み取ることはできません)。
CSRF 攻撃を防ぐには、ユーザーのログイン後にブラウザーが資格情報をサイレント モードで送信する認証プロトコルで偽造防止トークンを使用します。 これには、フォーム認証などの Cookie ベースの認証プロトコルと、基本認証やダイジェスト認証などのプロトコルが含まれます。
安全でないメソッド (POST、PUT、DELETE) に偽造防止トークンが必要です。 また、安全なメソッド (GET、HEAD) に副作用がないことを確認します。 さらに、CORS や JSONP などのクロスドメイン サポートを有効にしている場合、GET などの安全な方法でも CSRF 攻撃に対して脆弱になり、攻撃者は機密データを読み取ることができる可能性があります。
ASP.NET MVC の偽造防止トークン
Razor ページに偽造防止トークンを追加するには、HtmlHelper.AntiForgeryToken ヘルパー メソッドを使用します。
@using (Html.BeginForm("Manage", "Account")) {
@Html.AntiForgeryToken()
}
このメソッドは、非表示のフォーム フィールドを追加し、Cookie トークンも設定します。
CSRF 対策と AJAX
AJAX 要求は HTML フォーム データではなく JSON データを送信する場合があるため、フォーム トークンは AJAX 要求に対して問題である可能性があります。 1 つの解決策は、カスタム HTTP ヘッダーでトークンを送信することです。 次のコードでは、Razor 構文を使ってトークンを生成した後、AJAX 要求にトークンを追加しています。 トークンは、AntiForgery.GetTokens を呼び出すことによってサーバーで生成されます。
<script>
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
});
</script>
要求を処理するときは、要求ヘッダーからトークンを抽出します。 その後、AntiForgery.Validate メソッドを呼び出してトークンを検証します。 トークンが有効ではない場合、Validate メソッドは例外をスローします。
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}