ASP.NET Core でクロスサイト リクエスト フォージェリ (XSRF/CSRF) 攻撃を防止する

作成者: Fiyaz HasanRick AndersonSteve Smith

クロスサイト リクエスト フォージェリ (XSRF または CSRF とも呼ばれます) は、Web ホスト アプリに対する攻撃であり、悪意のある Web アプリがクライアント ブラウザーとそのブラウザーを信頼する Web アプリとの間の対話に、影響を与える可能性があります。 これらの攻撃は、Web ブラウザーが Web サイトへのすべての要求に対して何らかの種類の認証トークンを自動的に送信するために発生する可能性があります。 この形式の悪用は、攻撃でユーザーの以前に認証されたセッションが利用されるため、"ワンクリック攻撃" または "セッション ライディング" としても知られます。

CSRF 攻撃の例:

  1. ユーザーが、フォーム認証を使用して www.good-banking-site.com にサインインします。 サーバーはユーザーを認証し、認証 cookie を含む応答を発行します。 サイトは、有効な認証 cookie と共に受信したすべての要求を信頼するため、攻撃に対して脆弱です。

  2. ユーザーが悪意のあるサイト www.bad-crook-site.com にアクセスします。

    悪意のあるサイト には www.bad-crook-site.com 、次の例のような HTML フォームが含まれます。

    <h1>Congratulations! You're a Winner!</h1>
    <form action="https://good-banking-site.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    悪意のあるサイトではなく、脆弱なサイトにフォームの action が POST されていることに注意してください。 これが CSRF の "クロスサイト" 部分です。

  3. ユーザーが送信ボタンを選びます。 ブラウザーは要求を行い、要求されたドメイン www.good-banking-site.com に対する認証 cookie を自動的に含めます。

  4. 要求はユーザーの認証コンテキストを使用して www.good-banking-site.com サーバー上で実行され、認証されたユーザーが実行を許可されている任意のアクションを実行できます。

ユーザーがボタンを選んでフォームを送信するシナリオに加えて、悪意のあるサイトでは次のこともできます。

  • フォームを自動的に送信するスクリプトを実行する。
  • フォーム送信を AJAX 要求として送信する。
  • CSS を使用してフォームを非表示にする。

これらの代替シナリオでは、最初に悪意のあるサイトにアクセスすること以外に、ユーザーによる操作や入力は必要ありません。

HTTPS を使用しても CSRF 攻撃を防ぐことはできません。 悪意のあるサイトは、安全ではない要求を送信するのと同じように簡単に、https://www.good-banking-site.com/ 要求を送信できます。

一部の攻撃は GET 要求に応答するエンドポイントを対象にしており、その場合、イメージ タグを使用してアクションを実行できます。 この形式の攻撃は、イメージは許可するが JavaScript はブロックするフォーラム サイトでよく使用されます。 GET 要求で状態を変更するアプリ (変数またはリソースが変更される場合) は、悪意のある攻撃に対して脆弱です。 状態を変更する GET 要求は安全ではない。 ベスト プラクティスは、GET 要求の状態を変更しない方法です。

次の理由により、認証に cookie を使用する Web アプリに対して、CSRF 攻撃が行われる可能性があります。

  • Web アプリによって発行された cookie がブラウザーで格納されます。
  • 格納される cookie には、認証されたユーザーのセッション cookie が含まれます。
  • アプリへの要求がブラウザー内で生成された方法に関係なく、ブラウザーからは、ドメインに関連付けられているすべての cookie が要求ごとに Web アプリに送信されます。

ただし、CSRF 攻撃は、cookie の悪用に限定されません。 たとえば、基本認証やダイジェスト認証も脆弱です。 ユーザーが基本認証またはダイジェスト認証を使用してサインインすると、ブラウザーはセッションが終了するまで資格情報を†します。

†このコンテキストでは、 セッション は、ユーザーが認証されるクライアント側セッションを参照します。 サーバー側セッションや ASP.NET Core のセッション ミドルウェアとは無関係です。

ユーザーは、予防措置を講じて CSRF の脆弱性を防ぐことができます。

  • Web アプリの使用が完了したら、サインアウトします。
  • ブラウザーの cookie を定期的にクリアします。

ただし、CSRF の脆弱性は、根本的に、エンド ユーザーではなく Web アプリに関する問題です。

認証の基礎

Cookie ベースの認証は、一般的な認証形式です。 トークン ベースの認証システムは普及が広がっています (特にシングルページ アプリケーション (SPA) の場合)。

ユーザーは、ユーザー名とパスワードを使って認証を行うと、認証と承認に使用できる認証チケットが含まれるトークンを発行されます。 トークンは、クライアントが行 cookie うすべての要求で送信される として格納されます。 この cookie の生成と検証は、Cookie 認証ミドルウェアによって行われます。 ミドルウェア 、ユーザー プリンシパルを暗号化された にシリアル化します 。 後続の要求では、ミドルウェアによって が検証され、プリンシパルが再作成され、 プロパティに cookie プリンシパルが割り当 HttpContext.User てされます。

トークンベースの認証

認証されたユーザーには、トークンが発行されます (偽造防止トークンではありません)。 このトークンには、クレームまたはアプリで保持されているユーザー状態をアプリに示す参照トークンの形式で、ユーザー情報が含まれます。 ユーザーが認証を必要とするリソースにアクセスしようとすると、ベアラー トークンの形式で追加の承認ヘッダーを使用してトークンがアプリに送信されます。 この方法により、アプリはステートレスになります。 後続の要求ごとに、サーバー側の検証のために要求でトークンが渡されます。 このトークンは、"暗号化" されておらず、"エンコード" されています。 サーバーでは、トークンをデコードして情報にアクセスします。 後続の要求でトークンを送信するため、トークンはブラウザーのローカル ストレージに格納されます。 トークンがブラウザーのローカル ストレージに格納される場合は、CSRF の脆弱性について心配しないでください。 CSRF が問題になるのは、トークンが cookie に格納さるときです。 詳細については、次の問題に関するGitHubしてください SPA code sample adds two cookies

1 つのドメインでホストされている複数のアプリ

共有ホスティング環境は、セッション ハイジャック、ログイン CSRF、その他の攻撃に対して脆弱です。

example1.contoso.netexample2.contoso.net は異なるホストですが、*.contoso.net ドメインの下のホスト間には暗黙的な信頼関係があります。 この暗黙的な信頼関係により、信頼できないホストが互いの cookie に影響を与えることができる可能性があります (AJAX 要求を管理する同一オリジン ポリシーは、必ずしも HTTP cookie に適用されるとは限りません)。

同じドメインでホストされているアプリ間で信頼された cookie を悪用する攻撃は、ドメインを共有しないことで回避できます。 各アプリが独自のドメインでホストされている場合、悪用する暗黙的 cookie な信頼関係はありません。

ASP.NET Core でのアンチフォージェ ASP.NET Core

警告

ASP.NET Core では、ASP.NET Core データ保護を使用して偽造防止が実装されています。 サーバー ファームで動作するように、データ保護スタックを構成する必要があります。 詳細については、「データ保護の 構成」を参照してください

で次のいずれかの API が呼び出された場合、アンチフォージェリー ミドルウェアが Dependency Injection コンテナーに追加されます

FormTagHelper により HTML フォーム要素に偽造防止トークンが挿入されます。 Razor ファイルの次のマークアップによって、偽造防止トークンが自動的に生成されます。

<form method="post">
    <!-- ... -->
</form>

同様に、フォームの メソッドが GET ではない場合は、既定で、盗難防止 IHtmlHelper.BeginForm トークンが生成されます。

HTML フォーム要素に対する偽造防止トークンの自動生成は、<form> タグに method="post" 属性が含まれていて、次のいずれかが当てはまる場合に行われます。

  • action 属性が空です (action="")。
  • action 属性が指定されていません (<form method="post">)。

HTML フォーム要素に対する偽造防止トークンの自動生成は、無効にすることができます。

  • 偽造防止トークンを明示的に無効にするには、asp-antiforgery 属性を使います。

    <form method="post" asp-antiforgery="false">
        <!-- ... -->
    </form>
    
  • タグ ヘルパーの ! オプトアウト シンボルを使うと、フォーム要素がタグ ヘルパーからオプトアウトされます。

    <!form method="post">
        <!-- ... -->
    </!form>
    
  • ビューから FormTagHelper を削除します。 Razor ビューに次のディレクティブを追加することで、ビューから FormTagHelper を削除できます。

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

注意

Razor Pages は、XSRF/CSRF から自動的に保護されます。 詳細については、「XSRF/CSRF and Razor Pages」を参照してください。

CSRF 攻撃を防ぐ最も一般的な方法は、"シンクロナイザー トークン パターン" (STP) を使うことです。 STP は、ユーザーがフォーム データを含むページを要求するときに使われます。

  1. サーバーは、現在のユーザーの ID に関連付けられているトークンをクライアントに送信します。
  2. クライアントは、検証のためにトークンをサーバーに送り返します。
  3. サーバーが受け取ったトークンが、認証されたユーザーの ID と一致しない場合、要求は拒否されます。

トークンは一意で、予測できません。 トークンを使用して、一連の要求を適切にシーケンス処理することもできます (たとえば、ページ 1 ページ > 2 ページ 3 の要求シーケンスを確保します > )。 ASP.NET Core MVC と Razor Pages のテンプレートのすべてのフォームで、偽造防止トークンが生成されます。 次のビューの例のペアでは、盗難防止トークンが生成されます。

<form asp-action="Index" asp-controller="Home" method="post">
    <!-- ... -->
</form>

@using (Html.BeginForm("Index", "Home"))
{
    <!-- ... -->
}

タグ ヘルパーと HTML ヘルパー @Html.AntiForgeryToken を使用せず、偽造防止トークンを <form> 要素に明示的に追加します。

<form asp-action="Index" asp-controller="Home" method="post">
    @Html.AntiForgeryToken()

    <!-- ... -->
</form>

これらの各ケースでは、次の例のような非表示のフォームフィールドが ASP.NET Core によって追加されます。

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core には、偽造防止トークンを使用するための 3 つのフィルターが含まれています。

アンチ偽造を構成する AntiforgeryOptions

AntiforgeryOptionsAntiforgeryOptionsでカスタマイズします。

builder.Services.AddAntiforgery(options =>
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

CookieCookie クッキー ビルダー "class =" no loc "linktype =" absolute path ">builder クラスのプロパティを使用して、アンチ偽造プロパティを設定 Cookie し ます。

オプション 説明
cookie "class =" no loc "linktype =" absolute path ">Cookie 偽造防止 cookie の作成に使用される設定を決定します。
FormFieldName ビューで偽造防止トークンをレンダリングするために偽造防止システムによって使用される非表示フォーム フィールドの名前。
HeaderName 偽造防止システムによって使用されるヘッダーの名前。 null の場合、システムではフォーム データのみが考慮されます。
SuppressXFrameOptionsHeader X-Frame-Options ヘッダーの生成を抑制するかどうかを指定します。 既定では、ヘッダーは値 "SAMEORIGIN" を使用して生成されます。 既定値は false です。

詳細については、「 cookie authenticationoptions "class =" linktype = "absolute path" >Cookie authenticationoptions」を参照してください

アンチ偽造トークンを生成する方法 IAntiforgery

IAntiforgery 偽造防止機能を構成するための API を提供します。 IAntiforgery は、を使用して IAntiforgery で要求でき WebApplication.Services ます。 次の例では、アプリのホームページからミドルウェアを使用して、アンチ偽造トークンを生成し、として応答に送信し cookie ます。

app.UseRouting();

app.UseAuthorization();

var antiforgery = app.Services.GetRequiredService<IAntiforgery>();

app.Use((context, next) =>
{
    var requestPath = context.Request.Path.Value;

    if (string.Equals(requestPath, "/", StringComparison.OrdinalIgnoreCase)
        || string.Equals(requestPath, "/index.html", StringComparison.OrdinalIgnoreCase))
    {
        var tokenSet = antiforgery.GetAndStoreTokens(context);
        context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
            new CookieOptions { HttpOnly = false });
    }

    return next(context);
});

前の例では、 cookie という名前のを設定 XSRF-TOKEN しています。 クライアントはこれを読み取り、 cookie その値を AJAX 要求に関連付けられたヘッダーとして提供できます。 たとえば、Angular には、既定でという名前のを読み取る組み込みの XSRF 保護が組み込まれてい XSRF-TOKEN ます。

偽造防止の検証を要求する

Validateアンチ Forgerytokenアクションフィルターは、個々のアクション、コントローラー、またはグローバルに適用できます。 このフィルターが適用されているアクションに対して行われた要求は、要求に有効な偽造防止トークンが含まれている場合を除き、ブロックされます。

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index()
{
    // ...

    return RedirectToAction();
}

ValidateAntiForgeryToken 属性には、HTTP GET 要求を含む、マークするアクション メソッドへの要求に対するトークンが必要です。 ValidateAntiForgeryToken 属性がアプリのコントローラー全体に適用されている場合は、IgnoreAntiforgeryToken 属性でオーバーライドできます。

安全でない HTTP メソッドについてのみ偽造防止トークンを自動的に検証する

属性を広範に適用して属性でオーバーライドするのではなく ValidateAntiForgeryTokenIgnoreAntiforgeryTokenValidateAntiForgeryToken 属性を使用できます。 この属性は、ValidateAntiForgeryToken 属性と同じように動作しますが、次の HTTP メソッドを使用して行われた要求についてはトークンを要求しない点が異なります。

  • GET
  • HEAD
  • OPTIONS
  • TRACE

非 API のシナリオでは、AutoValidateAntiforgeryToken を広範に使うことをお勧めします。 この属性により、POST アクションが既定で保護されます。 代わりの方法は、ValidateAntiForgeryToken が個々のアクション メソッドに適用されない限り、既定で偽造防止トークンを無視することです。 このシナリオでは、POST アクション メソッドが誤って保護されないままになり、アプリが CSRF 攻撃に対して脆弱になる可能性が高くなります。 すべての POST で、偽造防止トークンを送信する必要があります。

API には、トークンの cookie 以外の部分を送信するための自動メカニズムはありません。 実装は、クライアント コードの実装によって異なる場合があります。 いくつかの例を次に示します。

クラス レベルの例:

[AutoValidateAntiforgeryToken]
public class HomeController : Controller

グローバルな例:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});

グローバルまたはコントローラーの偽造防止属性をオーバーライドする

IgnoreAntiforgeryToken フィルターは、特定のアクション (またはコントローラー) に対して偽造防止トークンを不要にするために使用されます。 このフィルターを適用すると、より高いレベル (グローバルまたはコントローラー上) で指定されている ValidateAntiForgeryToken および AutoValidateAntiforgeryToken フィルターがオーバーライドされます。

[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
    // ...

    return RedirectToAction();
}

認証後にトークンを更新する

ユーザーが認証された後、ユーザーをビューまたは Razor Pages ページにリダイレクトすることによって、トークンを更新する必要があります。

JavaScript、AJAX、SPA

従来の HTML ベースのアプリでは、偽造防止トークンは非表示のフォーム フィールドを使用してサーバーに渡されます。 最新の JavaScript ベースのアプリと SPA では、多くの要求がプログラムによって行われます。 これらの AJAX 要求では、他の手法 (要求ヘッダーや cookie など) を使ってトークンを送信できます。

cookie が、認証トークンの格納と、サーバーでの API 要求の認証に使用されている場合、CSRF が問題になる可能性があります。 ローカル ストレージがトークンの格納に使われている場合は、ローカル ストレージの値はすべての要求でサーバーに自動的に送信されないため、CSRF の脆弱性が軽減される可能性があります。 ローカルストレージを使用して、クライアントにアンチ偽造トークンを格納し、要求ヘッダーとしてトークンを送信することをお勧めします。

JavaScript

JavaScript とビューを使うと、ビュー内からサービスを使ってトークンを作成できます。 IAntiforgeryビューにサービスを挿入し、を呼び出し GetAndStoreTokens ます。

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery

@{
    ViewData["Title"] = "JavaScript";

    var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}

<input id="RequestVerificationToken" type="hidden" value="@requestToken" />

<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>

@section Scripts {
<script>
    document.addEventListener("DOMContentLoaded", () => {
        const resultElement = document.getElementById("result");

        document.getElementById("button").addEventListener("click", async () => {

            const response = await fetch("@Url.Action("FetchEndpoint")", {
                method: "POST",
                headers: {
                    RequestVerificationToken:
                        document.getElementById("RequestVerificationToken").value
                }
            });

            if (response.ok) {
                resultElement.innerText = await response.text();
            } else {
                resultElement.innerText = `Request Failed: ${response.status}`
            }
        });
    });
</script>
}

この方法を使用すると、サーバーからの cookie の設定やクライアントからの読み取りを、直接処理する必要がなくなります。

前の例では、JavaScript を使用して、AJAX POST ヘッダーの非表示フィールドの値を読み取っています。

また、JavaScript で cookie のトークンにアクセスし、cookie の内容を使用して、トークンの値でヘッダーを作成することもできます。 次の例では、要求トークンを JavaScript で判読できるに書き込み cookie ます。

context.Response.Cookies.Append("XSRF-TOKEN", tokenSet.RequestToken!,
    new CookieOptions { HttpOnly = false });

スクリプトがという要求ヘッダーでトークンを送信 X-XSRF-TOKEN する場合は、ヘッダーを検索するようにアンチ偽造サービスを構成し X-XSRF-TOKEN ます。

builder.Services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

次の例では、JavaScript を使用して、適切なヘッダーで AJAX 要求を作成しています。

// https://developer.mozilla.org/en-US/docs/web/api/document/cookie
const xsrfToken = document.cookie
    .split("; ")
    .find(row => row.startsWith("XSRF-TOKEN="))
    .split("=")[1];

const response = await fetch("/JavaScript/FetchEndpoint", {
    method: "POST",
    headers: { "X-XSRF-TOKEN": xsrfToken }
});

if (response.ok) {
    resultElement.innerText = await response.text();
} else {
    resultElement.innerText = `Request Failed: ${response.status}`
}

Windows 認証を使用するときは、cookie の場合と同じ方法で、アプリケーション エンドポイントを CSRF 攻撃から保護する必要があります。 ブラウザーは認証コンテキストをサーバーに暗黙的に送信するため、エンドポイントは CSRF 攻撃に対して保護する必要があります。

偽造防止を強化する

IAntiforgeryAdditionalDataProvider型を使用すると、開発者は、各トークンの追加データをラウンドトリップさせることで、CSRF システムの動作を拡張できます。 GetAdditionalDataメソッドは、フィールドトークンが生成されるたびに呼び出され、戻り値は生成されたトークン内に埋め込まれます。 実装者は、タイムスタンプ、nonce、またはその他の値を返し、を呼び出して ValidateAdditionalData 、トークンが検証されたときにこのデータを検証することができます。 クライアントのユーザー名は生成されたトークンに既に埋め込まれているので、この情報を含める必要はありません。 トークンに補足データが含まれていても、IAntiForgeryAdditionalDataProvider が構成されていない場合は、補足データは検証されません。

その他の技術情報

クロスサイト リクエスト フォージェリ (XSRF または CSRF とも呼ばれます) は、Web ホスト アプリに対する攻撃であり、悪意のある Web アプリがクライアント ブラウザーとそのブラウザーを信頼する Web アプリとの間の対話に、影響を与える可能性があります。 これらの攻撃は、Web ブラウザーが Web サイトへのすべての要求に対して何らかの種類の認証トークンを自動的に送信するために発生する可能性があります。 この形式の悪用は、攻撃でユーザーの以前に認証されたセッションが利用されるため、"ワンクリック攻撃" または "セッション ライディング" としても知られます。

CSRF 攻撃の例:

  1. ユーザーが、フォーム認証を使用して www.good-banking-site.com にサインインします。 サーバーはユーザーを認証し、認証 cookie を含む応答を発行します。 サイトは、有効な認証 cookie と共に受信したすべての要求を信頼するため、攻撃に対して脆弱です。

  2. ユーザーが悪意のあるサイト www.bad-crook-site.com にアクセスします。

    悪意のあるサイトには、 www.bad-crook-site.com 次の例のような HTML フォームが含まれています。

    <h1>Congratulations! You're a Winner!</h1>
    <form action="https://good-banking-site.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click to collect your prize!" />
    </form>
    

    悪意のあるサイトではなく、脆弱なサイトにフォームの action が POST されていることに注意してください。 これが CSRF の "クロスサイト" 部分です。

  3. ユーザーが送信ボタンを選びます。 ブラウザーは要求を行い、要求されたドメイン www.good-banking-site.com に対する認証 cookie を自動的に含めます。

  4. 要求はユーザーの認証コンテキストを使用して www.good-banking-site.com サーバー上で実行され、認証されたユーザーが実行を許可されている任意のアクションを実行できます。

ユーザーがボタンを選んでフォームを送信するシナリオに加えて、悪意のあるサイトでは次のこともできます。

  • フォームを自動的に送信するスクリプトを実行する。
  • フォーム送信を AJAX 要求として送信する。
  • CSS を使用してフォームを非表示にする。

これらの代替シナリオでは、最初に悪意のあるサイトにアクセスすること以外に、ユーザーによる操作や入力は必要ありません。

HTTPS を使用しても CSRF 攻撃を防ぐことはできません。 悪意のあるサイトは、安全ではない要求を送信するのと同じように簡単に、https://www.good-banking-site.com/ 要求を送信できます。

一部の攻撃は GET 要求に応答するエンドポイントを対象にしており、その場合、イメージ タグを使用してアクションを実行できます。 この形式の攻撃は、イメージは許可するが JavaScript はブロックするフォーラム サイトでよく使用されます。 GET 要求で状態を変更するアプリ (変数またはリソースが変更される場合) は、悪意のある攻撃に対して脆弱です。 状態を変更する GET 要求は安全ではありません。 GET 要求の状態を変更しないことをお勧めします。

次の理由により、認証に cookie を使用する Web アプリに対して、CSRF 攻撃が行われる可能性があります。

  • Web アプリによって発行された cookie がブラウザーで格納されます。
  • 格納される cookie には、認証されたユーザーのセッション cookie が含まれます。
  • アプリへの要求がブラウザー内で生成された方法に関係なく、ブラウザーからは、ドメインに関連付けられているすべての cookie が要求ごとに Web アプリに送信されます。

ただし、CSRF 攻撃は、cookie の悪用に限定されません。 たとえば、基本認証やダイジェスト認証も脆弱です。 ユーザーが基本認証またはダイジェスト認証を使用してサインインすると、セッション†が終了するまで、ブラウザーは資格情報を自動的に送信します。

†このコンテキストでは、 セッション とは、ユーザーが認証されるクライアント側のセッションを指します。 サーバー側セッションや ASP.NET Core のセッション ミドルウェアとは無関係です。

ユーザーは、予防措置を講じて CSRF の脆弱性を防ぐことができます。

  • Web apps の使用が完了したら、サインアウトします。
  • ブラウザーの cookie を定期的にクリアします。

ただし、CSRF の脆弱性は、根本的に、エンド ユーザーではなく Web アプリに関する問題です。

認証の基礎

Cookie ベースの認証は、一般的な認証形式です。 トークン ベースの認証システムは普及が広がっています (特にシングルページ アプリケーション (SPA) の場合)。

ユーザーは、ユーザー名とパスワードを使って認証を行うと、認証と承認に使用できる認証チケットが含まれるトークンを発行されます。 トークンは、 cookie クライアントが行うすべての要求と共に送信されるとして格納されます。 この cookie の生成と検証は、Cookie 認証ミドルウェアによって行われます。 ミドルウェアは、ユーザープリンシパルを暗号化されたにシリアル化し ます。 後続の要求では、ミドルウェアはを検証し、 cookie プリンシパルを再作成して、プリンシパルをプロパティに割り当て HttpContext.User ます。

トークンベースの認証

認証されたユーザーには、トークンが発行されます (偽造防止トークンではありません)。 このトークンには、クレームまたはアプリで保持されているユーザー状態をアプリに示す参照トークンの形式で、ユーザー情報が含まれます。 認証が必要なリソースにユーザーがアクセスしようとすると、トークンは、ベアラートークンの形式で追加の authorization ヘッダーを使用してアプリに送信されます。 この方法では、アプリはステートレスになります。 後続の要求ごとに、サーバー側の検証のために要求でトークンが渡されます。 このトークンは、"暗号化" されておらず、"エンコード" されています。 サーバーでは、トークンをデコードして情報にアクセスします。 後続の要求でトークンを送信するため、トークンはブラウザーのローカル ストレージに格納されます。 トークンがブラウザーのローカル ストレージに格納される場合は、CSRF の脆弱性について心配しないでください。 CSRF が問題になるのは、トークンが cookie に格納さるときです。 詳細については、GitHub の問題を参照してください SPA code sample adds two cookies

1 つのドメインでホストされている複数のアプリ

共有ホスティング環境は、セッション ハイジャック、ログイン CSRF、その他の攻撃に対して脆弱です。

example1.contoso.netexample2.contoso.net は異なるホストですが、*.contoso.net ドメインの下のホスト間には暗黙的な信頼関係があります。 この暗黙的な信頼関係により、信頼できないホストが互いの cookie に影響を与えることができる可能性があります (AJAX 要求を管理する同一オリジン ポリシーは、必ずしも HTTP cookie に適用されるとは限りません)。

同じドメインでホストされているアプリ間で信頼された cookie を悪用する攻撃は、ドメインを共有しないことで回避できます。 各アプリが独自のドメインでホストされている場合、 cookie 悪用に対する暗黙の信頼関係はありません。

ASP.NET Core の偽造防止の構成

警告

ASP.NET Core では、ASP.NET Core データ保護を使用して偽造防止が実装されています。 サーバー ファームで動作するように、データ保護スタックを構成する必要があります。 詳細については、「 データ保護の構成」を参照してください。

次のいずれかの Api がで呼び出されると、アンチ偽造ミドルウェアが 依存関係の注入 コンテナーに追加され ます。

ASP.NET Core 2.0 以降では、FormTagHelper により HTML フォーム要素に偽造防止トークンが挿入されます。 Razor ファイルの次のマークアップによって、偽造防止トークンが自動的に生成されます。

<form method="post">
    ...
</form>

同様に、 IHtmlHelper.BeginForm フォームのメソッドが GET でない場合、は既定で偽造防止トークンを生成します。

HTML フォーム要素に対する偽造防止トークンの自動生成は、<form> タグに method="post" 属性が含まれていて、次のいずれかが当てはまる場合に行われます。

  • action 属性が空です (action="")。
  • action 属性が指定されていません (<form method="post">)。

HTML フォーム要素に対する偽造防止トークンの自動生成は、無効にすることができます。

  • 偽造防止トークンを明示的に無効にするには、asp-antiforgery 属性を使います。

    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • タグ ヘルパーの ! オプトアウト シンボルを使うと、フォーム要素がタグ ヘルパーからオプトアウトされます。

    <!form method="post">
        ...
    </!form>
    
  • ビューから FormTagHelper を削除します。 Razor ビューに次のディレクティブを追加することで、ビューから FormTagHelper を削除できます。

    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

注意

Razor Pages は XSRF/CSRF から自動的に保護されます。 詳細については、「XSRF/CSRF and Razor Pages」を参照してください。

CSRF 攻撃を防ぐ最も一般的な方法は、"シンクロナイザー トークン パターン" (STP) を使うことです。 STP は、ユーザーがフォーム データを含むページを要求するときに使われます。

  1. サーバーは、現在のユーザーの ID に関連付けられているトークンをクライアントに送信します。
  2. クライアントは、検証のためにトークンをサーバーに送り返します。
  3. サーバーが受け取ったトークンが、認証されたユーザーの ID と一致しない場合、要求は拒否されます。

トークンは一意で、予測できません。 トークンを使用して、一連の要求を適切にシーケンス処理することもできます (たとえば、ページ 1 > ページ 2 > ページ 3)。 ASP.NET Core MVC と Razor Pages のテンプレートのすべてのフォームで、偽造防止トークンが生成されます。 次の2つのビュー例では、偽造防止トークンが生成されます。

<form asp-controller="Todo" asp-action="Create" method="post">
    ...
</form>

@using (Html.BeginForm("Create", "Todo"))
{
    ...
}

タグ ヘルパーと HTML ヘルパー @Html.AntiForgeryToken を使用せず、偽造防止トークンを <form> 要素に明示的に追加します。

<form action="/" method="post">
    @Html.AntiForgeryToken()
</form>

これらの各ケースでは、次の例のような非表示のフォームフィールドが ASP.NET Core によって追加されます。

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core には、偽造防止トークンを使用するための 3 つのフィルターが含まれています。

偽造防止オプション

カスタマイズ AntiforgeryOptionsStartup.ConfigureServices :

services.AddAntiforgery(options => 
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

CookieCookie クッキー ビルダー "class =" no loc "linktype =" absolute path ">builder クラスのプロパティを使用して、アンチ偽造プロパティを設定 Cookie し ます。

オプション 説明
cookie "class =" no loc "linktype =" absolute path ">Cookie 偽造防止 cookie の作成に使用される設定を決定します。
FormFieldName ビューで偽造防止トークンをレンダリングするために偽造防止システムによって使用される非表示フォーム フィールドの名前。
HeaderName 偽造防止システムによって使用されるヘッダーの名前。 null の場合、システムではフォーム データのみが考慮されます。
SuppressXFrameOptionsHeader X-Frame-Options ヘッダーの生成を抑制するかどうかを指定します。 既定では、ヘッダーは値 "SAMEORIGIN" を使用して生成されます。 既定値は false です。

詳細については、「 cookie authenticationoptions "class =" linktype = "absolute path" >Cookie authenticationoptions」を参照してください

IAntiforgery を使用して偽造防止機能を構成する

IAntiforgery 偽造防止機能を構成するための API を提供します。 IAntiforgery は、Startup クラスの Configure メソッドで要求できます。 次の例では、アプリのホームページからミドルウェアを使用して、アンチ偽造トークンを生成し、として応答に送信し cookie ます (この記事で後述する既定の Angular 名前付け規則を使用)。

public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
    app.Use(next => context =>
    {
        string path = context.Request.Path.Value;

        if (
            string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
            string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
        {
            // The request token can be sent as a JavaScript-readable cookie, 
            // and Angular uses it by default.
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, 
                new CookieOptions() { HttpOnly = false });
        }

        return next(context);
    });
}

偽造防止の検証を要求する

ValidateAntiForgeryToken は、個々のアクションやコントローラー、またはグローバルに適用できるアクション フィルターです。 このフィルターが適用されているアクションに対して行われた要求は、要求に有効な偽造防止トークンが含まれていない限り、ブロックされます。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
    ManageMessageId? message = ManageMessageId.Error;
    var user = await GetCurrentUserAsync();

    if (user != null)
    {
        var result = 
            await _userManager.RemoveLoginAsync(
                user, account.LoginProvider, account.ProviderKey);

        if (result.Succeeded)
        {
            await _signInManager.SignInAsync(user, isPersistent: false);
            message = ManageMessageId.RemoveLoginSuccess;
        }
    }

    return RedirectToAction(nameof(ManageLogins), new { Message = message });
}

ValidateAntiForgeryToken 属性には、HTTP GET 要求を含む、マークするアクション メソッドへの要求に対するトークンが必要です。 ValidateAntiForgeryToken 属性がアプリのコントローラー全体に適用されている場合は、IgnoreAntiforgeryToken 属性でオーバーライドできます。

注意

ASP.NET Core では、GET 要求への自動的な偽造防止トークンの追加はサポートされていません。

安全でない HTTP メソッドについてのみ偽造防止トークンを自動的に検証する

ASP.NET Core アプリでは、安全な HTTP メソッド (GET、HEAD、OPTIONS、TRACE) には偽造防止トークンは生成されません。 属性を広範に適用して属性でオーバーライドするのではなく ValidateAntiForgeryTokenIgnoreAntiforgeryTokenValidateAntiForgeryToken 属性を使用できます。 この属性は、ValidateAntiForgeryToken 属性と同じように動作しますが、次の HTTP メソッドを使用して行われた要求についてはトークンを要求しない点が異なります。

  • GET
  • HEAD
  • OPTIONS
  • TRACE

非 API のシナリオでは、AutoValidateAntiforgeryToken を広範に使うことをお勧めします。 この属性により、POST アクションが既定で保護されます。 代わりの方法は、ValidateAntiForgeryToken が個々のアクション メソッドに適用されない限り、既定で偽造防止トークンを無視することです。 このシナリオでは、POST アクション メソッドが誤って保護されないままになり、アプリが CSRF 攻撃に対して脆弱になる可能性が高くなります。 すべての POST で、偽造防止トークンを送信する必要があります。

API には、トークンの cookie 以外の部分を送信するための自動メカニズムはありません。 実装は、クライアント コードの実装によって異なる場合があります。 いくつかの例を次に示します。

クラス レベルの例:

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{

グローバルな例:

services.AddControllersWithViews(options =>
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));

グローバルまたはコントローラーの偽造防止属性をオーバーライドする

IgnoreAntiforgeryToken フィルターは、特定のアクション (またはコントローラー) に対して偽造防止トークンを不要にするために使用されます。 このフィルターを適用すると、より高いレベル (グローバルまたはコントローラー上) で指定されている ValidateAntiForgeryToken および AutoValidateAntiforgeryToken フィルターがオーバーライドされます。

[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
    [HttpPost]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
    {
        // no antiforgery token required
    }
}

認証後にトークンを更新する

ユーザーが認証された後、ユーザーをビューまたは Razor Pages ページにリダイレクトすることによって、トークンを更新する必要があります。

JavaScript、AJAX、SPA

従来の HTML ベースのアプリでは、偽造防止トークンは非表示のフォーム フィールドを使用してサーバーに渡されます。 最新の JavaScript ベースのアプリと SPA では、多くの要求がプログラムによって行われます。 これらの AJAX 要求では、他の手法 (要求ヘッダーや cookie など) を使ってトークンを送信できます。

cookie が、認証トークンの格納と、サーバーでの API 要求の認証に使用されている場合、CSRF が問題になる可能性があります。 ローカル ストレージがトークンの格納に使われている場合は、ローカル ストレージの値はすべての要求でサーバーに自動的に送信されないため、CSRF の脆弱性が軽減される可能性があります。 ローカルストレージを使用して、クライアントにアンチ偽造トークンを格納し、要求ヘッダーとしてトークンを送信することをお勧めします。

JavaScript

JavaScript とビューを使うと、ビュー内からサービスを使ってトークンを作成できます。 IAntiforgeryビューにサービスを挿入し、を呼び出し GetAndStoreTokens ます。

@{
    ViewData["Title"] = "AJAX Demo";
}
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

<input type="hidden" id="RequestVerificationToken" 
       name="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">

<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<div class="row">
    <p><input type="button" id="antiforgery" value="Antiforgery"></p>
    <script>
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
            if (xhttp.readyState == XMLHttpRequest.DONE) {
                if (xhttp.status == 200) {
                    alert(xhttp.responseText);
                } else {
                    alert('There was an error processing the AJAX request.');
                }
            }
        };

        document.addEventListener('DOMContentLoaded', function() {
            document.getElementById("antiforgery").onclick = function () {
                xhttp.open('POST', '@Url.Action("Antiforgery", "Home")', true);
                xhttp.setRequestHeader("RequestVerificationToken", 
                    document.getElementById('RequestVerificationToken').value);
                xhttp.send();
            }
        });
    </script>
</div>

この方法を使用すると、サーバーからの cookie の設定やクライアントからの読み取りを、直接処理する必要がなくなります。

前の例では、JavaScript を使用して、AJAX POST ヘッダーの非表示フィールドの値を読み取っています。

また、JavaScript で cookie のトークンにアクセスし、cookie の内容を使用して、トークンの値でヘッダーを作成することもできます。

context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, 
    new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });

X-CSRF-TOKEN という名前のヘッダーでトークンを送信することがスクリプトで要求されている場合は、X-CSRF-TOKEN ヘッダーを検索するように偽造防止サービスを構成します。

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

次の例では、JavaScript を使用して、適切なヘッダーで AJAX 要求を作成しています。

function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) === ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

var csrfToken = getCookie("CSRF-TOKEN");

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
    if (xhttp.readyState === XMLHttpRequest.DONE) {
        if (xhttp.status === 204) {
            alert('Todo item is created successfully.');
        } else {
            alert('There was an error processing the AJAX request.');
        }
    }
};
xhttp.open('POST', '/api/items', true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "name": "Learn C#" }));

AngularJS

AngularJS では、CSRF に対処するために規則が使用されます。 サーバーが XSRF-TOKEN という名前で cookie を送信した場合、AngularJS $http サービスにより、サーバーに要求が送信されるときに、ヘッダーに cookie の値が追加されます。 このプロセスは自動です。 クライアントは、ヘッダーを明示的に設定する必要はありません。 ヘッダーの名前は X-XSRF-TOKEN です。 サーバーでこのヘッダーを検出し、その内容を検証する必要があります。

アプリケーションの起動時に ASP.NET Core API でこの規則を処理するには:

  • XSRF-TOKEN という名前の cookie でトークンを提供するようにアプリを構成します。
  • X-XSRF-TOKEN という名前のヘッダーを探すように、偽造防止サービスを構成します。
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
    app.Use(next => context =>
    {
        string path = context.Request.Path.Value;

        if (
            string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
            string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
        {
            // The request token can be sent as a JavaScript-readable cookie, 
            // and Angular uses it by default.
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, 
                new CookieOptions() { HttpOnly = false });
        }

        return next(context);
    });
}

public void ConfigureServices(IServiceCollection services)
{
    // Angular's default header name for sending the XSRF token.
    services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
}

Windows 認証を使用するときは、cookie の場合と同じ方法で、アプリケーション エンドポイントを CSRF 攻撃から保護する必要があります。 ブラウザーは認証コンテキストをサーバーに暗黙的に送信するため、エンドポイントは CSRF 攻撃に対して保護する必要があります。

偽造防止を強化する

IAntiforgeryAdditionalDataProvider型を使用すると、開発者は、各トークンの追加データをラウンドトリップさせることで、CSRF システムの動作を拡張できます。 GetAdditionalDataメソッドは、フィールドトークンが生成されるたびに呼び出され、戻り値は生成されたトークン内に埋め込まれます。 実装者は、タイムスタンプ、nonce、またはその他の値を返し、を呼び出して ValidateAdditionalData 、トークンが検証されたときにこのデータを検証することができます。 クライアントのユーザー名は生成されたトークンに既に埋め込まれているので、この情報を含める必要はありません。 トークンに補足データが含まれていても、IAntiForgeryAdditionalDataProvider が構成されていない場合は、補足データは検証されません。

その他の技術情報