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);
}