ASP.NET MVC アプリケーションでのクロスサイト リクエスト フォージェリ (CSRF) 攻撃の防止

クロスサイト リクエスト フォージェリ (CSRF) は、悪意のあるサイトが、ユーザーが現在ログインしている脆弱なサイトに要求を送信する攻撃です

CSRF 攻撃の例を次に示します。

  1. ユーザーはフォーム認証を使用してログイン www.example.com します。

  2. サーバーはユーザーを認証します。 サーバーからの応答には、認証 Cookie が含まれています。

  3. ログアウトせずに、ユーザーは悪意のある 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>
    

    フォーム アクションは、悪意のあるサイトではなく、脆弱なサイトに投稿されます。 これが CSRF の "クロスサイト" 部分です。

  4. ユーザーが [送信] ボタンをクリックします。 ブラウザーには、要求を含む認証 Cookie が含まれています。

  5. 要求は、ユーザーの認証コンテキストを使用してサーバー上で実行され、認証されたユーザーが実行できるあらゆる操作を実行できます。

この例では、ユーザーがフォーム ボタンをクリックする必要がありますが、悪意のあるページでも、フォームを自動的に送信するスクリプトを簡単に実行できます。 さらに、SSL を使用しても、悪意のあるサイトから "https://" 要求が送信される可能性があるため、CSRF 攻撃を防ぐことはありません。

通常、認証に Cookie を使用する Web サイトに対して CSRF 攻撃が発生する可能性があります。これは、ブラウザーが関連するすべての Cookie を宛先 Web サイトに送信するためです。 ただし、CSRF 攻撃は Cookie の悪用に限定されません。 たとえば、基本認証やダイジェスト認証も脆弱です。 ユーザーが基本認証またはダイジェスト認証でログインした後。 セッションが終了するまで、ブラウザーによって資格情報が自動的に送信されます。

偽造防止トークン

CSRF 攻撃を防ぐために、ASP.NET MVC では、要求検証トークンとも呼ばれる偽造防止 トークンが使用されます

  1. クライアントは、フォームを含む HTML ページを要求します。
  2. サーバーには、応答に 2 つのトークンが含まれています。 1 つのトークンが Cookie として送信されます。 もう 1 つは非表示のフォーム フィールドに配置されます。 敵対者が値を推測できないように、トークンはランダムに生成されます。
  3. クライアントがフォームを送信するときは、両方のトークンをサーバーに送り返す必要があります。 クライアントは Cookie トークンを Cookie として送信し、フォーム データ内にフォーム トークンを送信します。 (ブラウザー クライアントは、ユーザーがフォームを送信するときに自動的にこれを行います。
  4. 要求に両方のトークンが含まれていない場合、サーバーは要求を禁止します。

非表示のフォーム トークンを含む 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);
}