ASP.NET Core で SameSite の cookie を使用する

作成者: Rick Anderson

SameSite は、クロスサイト リクエスト フォージェリ (CSRF) 攻撃に対して何らかの保護を提供するように設計された IETF のドラフト標準です。 最初のドラフトは 2016 年に作成され、2019 年にはドラフト標準が更新されました。 更新された標準は、以前の標準と下位互換性がありません。最も顕著な違いは次のとおりです。

  • SameSite ヘッダーのない Cookie は、既定で SameSite=Lax として扱われます。
  • クロスサイトでの SameSite=None の使用を許可するには、cookie を使う必要があります。
  • SameSite=None をアサートする Cookie も、Secure としてマークする必要があります。
  • <iframe> を使用するアプリケーションでは、<iframe> がクロスサイトのシナリオとして扱われるため、sameSite=Lax または sameSite=Strictcookies に関する問題が発生する可能性があります。
  • SameSite=None2016 年の標準では許可されず、一部の実装では、そのような cookie は SameSite=Strict として処理されます。 このドキュメントの「古いブラウザーのサポート」をご覧ください。

SameSite=Lax の設定は、ほとんどのアプリケーションの cookie で機能します。 OpenID Connect (OIDC) やWS-Federation など、一部の認証形式では、既定で POST ベースのリダイレクトになります。 POST ベースのリダイレクトによって SameSite のブラウザー保護がトリガーされるため、これらのコンポーネントについては SameSite が無効になります。 要求のフロー方法の違いにより、ほとんどの OAuth ログインは影響を受けません。

cookie を生成する各 ASP.NET Core コンポーネントで、SameSite が適切かどうかを決定する必要があります。

SameSite と Identity

ASP.NET Core Identity は、IFrames または OpenIdConnect 統合のような高度なシナリオを除き、SameSite cookies の影響をほとんど受けません。

Identity を使用する場合は、cookie プロバイダーの追加や services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) の呼び出しを "行わないでください"。これらは、Identity によって行われます。

SameSite のテスト サンプル コード

次のサンプルをダウンロードしてテストできます。

サンプル ドキュメント
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages の SameSite cookie のサンプル

sameSite 属性の .NET Core によるサポート

.NET Core では、SameSite の 2019 ドラフト標準がサポートされています。 開発者は、プログラムで HttpCookie.SameSite プロパティを使用して、sameSite 属性の値を制御できます。 SameSite プロパティを StrictLax、または None に設定すると、それらの値が cookie でネットワークに書き込まれます。 SameSiteMode.Unspecified に設定すると、sameSite を cookie で送信してはならないことを示します。

    var cookieOptions = new CookieOptions
    {
        // Set the secure flag, which Chrome's changes will require for SameSite none.
        // Note this will also require you to be running on HTTPS.
        Secure = true,

        // Set the cookie to HTTP only which is good practice unless you really do need
        // to access it client side in scripts.
        HttpOnly = true,

        // Add the SameSite attribute, this will emit the attribute with a value of none.
        SameSite = SameSiteMode.None

        // The client should follow its default cookie policy.
        // SameSite = SameSiteMode.Unspecified
    };

    // Add the cookie to the response cookie collection
    Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
}

SameSite での API の使用

HttpContext.Response.Cookies.Append の既定値は Unspecified です。つまり、SameSite 属性は cookie に追加されず、クライアントでは既定の動作が使用されます (新しいブラウザーの場合は Lax、古いブラウザーの場合は None)。 次のコードでは、cookie の SameSite 値を SameSiteMode.Lax に変更する方法が示されています。

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

cookie を生成するすべての ASP.NET Core コンポーネントでは、シナリオに適した設定で、前の既定値をオーバーライドします。 オーバーライドされた前の既定値は変更されません。

コンポーネント cookie Default
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie Authentication CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 以降では、次の SameSite のサポートが提供されます。

  • SameSite=None を生成する SameSiteMode.None の動作を再定義します
  • SameSite 属性を省略するための新しい値 SameSiteMode.Unspecified を追加します。
  • すべての cookie API の既定値を Unspecified にします。 cookie を使用する一部のコンポーネントで、シナリオに対するさらに固有の値を設定します。 例については、上記の表をご覧ください。

ASP.NET Core 3.0 以降では、クライアントの既定値との競合を回避するため、SameSite の既定値が変更されました。 次の API では、これらの cookie に SameSite 属性が生成されないように、既定値が SameSiteMode.Lax から -1 に変更されました。

履歴と変更

SameSite のサポートは、2016 年のドラフト標準を使用する ASP.NET Core 2.0 で最初に実装されました。 2016 年の標準はオプトインでした。 ASP.NET Core は、複数の cookie を Lax に既定で設定することによりオプトインしました。 認証に関するいくつかのイシューが発生した後、SameSite のほとんどの使用は無効にされました

2019 年 11 月にパッチが発行されて、2016 標準から 2019 標準に更新されました。 SameSite 仕様の 2019 ドラフト:

  • 2016 ドラフトとの下位互換性はありません。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。
  • cookie を指定すると、既定で SameSite=Lax として扱われます。
  • クロスサイト配信を有効にするために明示的に SameSite=None アサートする cookie の指定は、Secure としてマークする必要があります。 None は、オプトアウトする新しいエントリです。
  • ASP.NET Core 2.1、2.2、3.0 に対して発行されたパッチによってサポートされます。 ASP.NET Core 3.1 以降には、SameSite の追加サポートがあります。
  • 2020 年 2 月に、Chrome によって既定で有効にされる予定です。 この標準へのブラウザーの移行は、2019 年に開始されました。

SameSite の 2016 ドラフト標準から 2019 ドラフト標準への変更によって影響を受ける API

古いブラウザーのサポート

2016 SameSite 標準では、不明な値を SameSite=Strict 値として扱う必要がありました。 2016 SameSite 標準をサポートする古いブラウザーからアクセスされるアプリは、取得した SameSite プロパティの値が None である場合、機能しなくなる可能性があります。 Web アプリで古いブラウザーをサポートする場合は、ブラウザーの検出を実装する必要があります。 User-Agents の値は揮発性が高く、頻繁に変更されるため、ASP.NET Core にはブラウザーの検出は実装されていません。 Microsoft.AspNetCore.CookiePolicy の拡張ポイントを使用すると、User-Agent 固有のロジックを接続できます。

Program.cs では、UseAuthentication または cookie を書き込む "すべての" メソッドを呼び出す前に、UseCookiePolicy を呼び出すコードを追加します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.OnAppendCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

    builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseCookiePolicy();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

次の強調表示されたコードのようなコードを、Program.cs に追加します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
    options.OnAppendCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    options.OnDeleteCookie = cookieContext =>
        CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});

void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

    builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseCookiePolicy();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

前のサンプルの MyUserAgentDetectionLib.DisallowsSameSiteNone は、ユーザー エージェントで SameSite None がサポートされていないかどうかを検出する、ユーザー提供のライブラリです。

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

次のコードは、DisallowsSameSiteNone メソッドのサンプルです。

警告

次のコードは、デモのみを目的としたものです。

  • 完全なものと考えないでください。
  • メンテナンスもサポートもされません。
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(userAgent))
        return false;
    
    // Cover all iOS based browsers here. This includes:
    // - Safari on iOS 12 for iPhone, iPod Touch, iPad
    // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
    // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
    // All of which are broken by SameSite=None, because they use the iOS networking
    // stack.
    if (userAgent.Contains("CPU iPhone OS 12") ||
        userAgent.Contains("iPad; CPU OS 12"))
    {
        return true;
    }

    // Cover Mac OS X based browsers that use the Mac OS networking stack. 
    // This includes:
    // - Safari on Mac OS X.
    // This does not include:
    // - Chrome on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
        userAgent.Contains("Version/") && userAgent.Contains("Safari"))
    {
        return true;
    }

    // Cover Chrome 50-69, because some versions are broken by SameSite=None, 
    // and none in this range require it.
    // Note: this covers some pre-Chromium Edge versions, 
    // but pre-Chromium Edge does not require SameSite=None.
    if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
    {
        return true;
    }

    return false;
}

アプリで SameSite の問題をテストする

サードパーティ ログインなどを介してリモート サイトとやり取りするアプリでは、以下を行う必要があります。

新しい SameSite 動作にオプトインできるバージョンのクライアントを使って、Web アプリをテストします。 Chrome、Firefox、Chromium Edge のいずれにも、テストに使用できる新しいオプトイン機能フラグがあります。 アプリで SameSite のパッチを適用した後、古いクライアント バージョン (特に Safari) でテストします。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。

Chrome についてテストする

Chrome 78 以降では、一時的な軽減策が設定されているため、誤解を招く結果が得られます。 Chrome 78 以降の一時的な軽減策により、2 分より短い cookie が許可されます。 適切なテスト フラグが有効にされた Chrome 76 または 77 では、より正確な結果が提供されます。 SameSite の新しい動作をテストするには、chrome://flags/#same-site-by-default-cookiesEnabled に切り替えます。 古いバージョンの Chrome (75 以前) では、新しい設定 None を使うと失敗することが報告されています。 このドキュメントの「古いブラウザーのサポート」をご覧ください。

Google では、以前のバージョンの Chrome は提供されていません。 Chromium のダウンロードに関するページの手順に従って、古いバージョンの Chrome をテストしてください。 Chrome の古いバージョンを検索して示されるリンクからは、Chrome をダウンロードしないでください

Canary バージョン 80.0.3975.0 以降では、新しいフラグ --enable-features=SameSiteDefaultChecksMethodRigorously を使用してテストのために Lax+POST の一時的な軽減策を無効にし、軽減策が削除された機能の最終的な状態でサイトとサービスをテストすることができます。 詳しくは、The Chromium Projects の「SameSite Updates (SameSite の更新)」をご覧ください

Safari についてテストする

Safari 12 では以前のドラフトが厳密に実装されており、cookie が新しい値 None になっていると失敗します。 None は、このドキュメントの「古いブラウザーのサポート」のブラウザー検出コードを使用して回避されます。 MSAL、ADAL、または使用しているライブラリを使用して、Safari 12、Safari 13、WebKit ベースの OS スタイルのログインをテストします。 この問題は、基盤の OS バージョンによって変わります。 OSX Mojave (10.14) および iOS 12 には、SameSite の新しい動作との互換性の問題があることがわかっています。 OS を OSX Catalina (10.15) または iOS 13 にアップグレードすると、問題は解決します。 現在、Safari には新しい仕様の動作をテストするためのオプトイン フラグがありません。

Firefox についてテストする

Firefox による新しい標準のサポートは、バージョン 68 以降で、機能フラグ network.cookie.sameSite.laxByDefault を指定して about:config ページでオプトインすることでテストできます。 以前のバージョンの Firefox では、互換性の問題は報告されていません。

Edge ブラウザーについてテストする

Edge では、SameSite の古い標準がサポートされています。 Edge バージョン 44 には、新しい標準に関する既知の互換性の問題はありません。

Edge (Chromium) についてテストする

SameSite のフラグは、edge://flags/#same-site-by-default-cookies ページで設定されます。 Edge Chromium では互換性の問題は検出されませんでした。

Electron についてテストする

Electron の複数のバージョンには、Chromium の古いバージョンが含まれています。 たとえば、Teams で使用されている Electron のバージョンは Chromium 66 であり、以前の動作を示しています。 お使いの製品に使用されている Electron のバージョンを使って、互換性テストを実行する必要があります。 「古いブラウザーのサポート」をご覧ください。

その他の技術情報

サンプル ドキュメント
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages の SameSite cookie のサンプル

次のサンプルをダウンロードしてテストできます。

サンプル ドキュメント
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages の SameSite cookie のサンプル

sameSite 属性の .NET Core によるサポート

.NET Core 3.1 以降では、SameSite の 2019 ドラフト標準がサポートされています。 開発者は、プログラムで HttpCookie.SameSite プロパティを使用して、sameSite 属性の値を制御できます。 SameSite プロパティを Strict、Lax、または None に設定すると、それらの値が cookie でネットワークに書き込まれます。 それを (SameSiteMode)(-1) に設定すると、sameSite 属性を cookie でネットワークに含めてはならないことを示します

var cookieOptions = new CookieOptions
{
    // Set the secure flag, which Chrome's changes will require for SameSite none.
    // Note this will also require you to be running on HTTPS.
    Secure = true,

    // Set the cookie to HTTP only which is good practice unless you really do need
    // to access it client side in scripts.
    HttpOnly = true,

    // Add the SameSite attribute, this will emit the attribute with a value of none.
    // To not emit the attribute at all set
    // SameSite = (SameSiteMode)(-1)
    SameSite = SameSiteMode.None
};

// Add the cookie to the response cookie collection
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);

.NET Core 3.1 以降では、更新された SameSite 値がサポートされ、追加の列挙値 SameSiteMode.UnspecifiedSameSiteMode 列挙型に追加されます。 この新しい値は、sameSite を cookie で送信してはならないことを示します。

SameSite での API の使用

HttpContext.Response.Cookies.Append の既定値は Unspecified です。つまり、SameSite 属性は cookie に追加されず、クライアントでは既定の動作が使用されます (新しいブラウザーの場合は Lax、古いブラウザーの場合は None)。 次のコードでは、cookie の SameSite 値を SameSiteMode.Lax に変更する方法が示されています。

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

cookie を生成するすべての ASP.NET Core コンポーネントでは、シナリオに適した設定で、前の既定値をオーバーライドします。 オーバーライドされた前の既定値は変更されません。

コンポーネント cookie Default
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie Authentication CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 以降では、次の SameSite のサポートが提供されます。

  • SameSite=None を生成する SameSiteMode.None の動作を再定義します
  • SameSite 属性を省略するための新しい値 SameSiteMode.Unspecified を追加します。
  • すべての cookie API の既定値を Unspecified にします。 cookie を使用する一部のコンポーネントで、シナリオに対するさらに固有の値を設定します。 例については、上記の表をご覧ください。

ASP.NET Core 3.0 以降では、クライアントの既定値との競合を回避するため、SameSite の既定値が変更されました。 次の API では、これらの cookie に SameSite 属性が生成されないように、既定値が SameSiteMode.Lax から -1 に変更されました。

履歴と変更

SameSite のサポートは、2016 年のドラフト標準を使用する ASP.NET Core 2.0 で最初に実装されました。 2016 年の標準はオプトインでした。 ASP.NET Core は、複数の cookie を Lax に既定で設定することによりオプトインしました。 認証に関するいくつかのイシューが発生した後、SameSite のほとんどの使用は無効にされました

2019 年 11 月にパッチが発行されて、2016 標準から 2019 標準に更新されました。 SameSite 仕様の 2019 ドラフト:

  • 2016 ドラフトとの下位互換性はありません。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。
  • cookie を指定すると、既定で SameSite=Lax として扱われます。
  • クロスサイト配信を有効にするために明示的に SameSite=None アサートする cookie の指定は、Secure としてマークする必要があります。 None は、オプトアウトする新しいエントリです。
  • ASP.NET Core 2.1、2.2、3.0 に対して発行されたパッチによってサポートされます。 ASP.NET Core 3.1 には、SameSite の追加サポートがあります。
  • 2020 年 2 月に、Chrome によって既定で有効にされる予定です。 この標準へのブラウザーの移行は、2019 年に開始されました。

SameSite の 2016 ドラフト標準から 2019 ドラフト標準への変更によって影響を受ける API

古いブラウザーのサポート

2016 SameSite 標準では、不明な値を SameSite=Strict 値として扱う必要がありました。 2016 SameSite 標準をサポートする古いブラウザーからアクセスされるアプリは、取得した SameSite プロパティの値が None である場合、機能しなくなる可能性があります。 Web アプリで古いブラウザーをサポートする場合は、ブラウザーの検出を実装する必要があります。 User-Agents の値は揮発性が高く、頻繁に変更されるため、ASP.NET Core にはブラウザーの検出は実装されていません。 Microsoft.AspNetCore.CookiePolicy の拡張ポイントを使用すると、User-Agent 固有のロジックを接続できます。

Startup.Configure では、UseAuthentication または cookie を書き込む "すべての" メソッドを呼び出す前に、UseCookiePolicy を呼び出すコードを追加します。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Startup.ConfigureServices では、次のようなコードを追加します。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
        options.OnAppendCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });

    services.AddRazorPages();
}

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = SameSiteMode.Unspecified;
        }
    }
}

前のサンプルの MyUserAgentDetectionLib.DisallowsSameSiteNone は、ユーザー エージェントで SameSite None がサポートされていないかどうかを検出する、ユーザー提供のライブラリです。

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

次のコードは、DisallowsSameSiteNone メソッドのサンプルです。

警告

次のコードは、デモのみを目的としたものです。

  • 完全なものと考えないでください。
  • メンテナンスもサポートもされません。
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(userAgent))
        return false;
    
    // Cover all iOS based browsers here. This includes:
    // - Safari on iOS 12 for iPhone, iPod Touch, iPad
    // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
    // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
    // All of which are broken by SameSite=None, because they use the iOS networking
    // stack.
    if (userAgent.Contains("CPU iPhone OS 12") ||
        userAgent.Contains("iPad; CPU OS 12"))
    {
        return true;
    }

    // Cover Mac OS X based browsers that use the Mac OS networking stack. 
    // This includes:
    // - Safari on Mac OS X.
    // This does not include:
    // - Chrome on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
        userAgent.Contains("Version/") && userAgent.Contains("Safari"))
    {
        return true;
    }

    // Cover Chrome 50-69, because some versions are broken by SameSite=None, 
    // and none in this range require it.
    // Note: this covers some pre-Chromium Edge versions, 
    // but pre-Chromium Edge does not require SameSite=None.
    if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
    {
        return true;
    }

    return false;
}

アプリで SameSite の問題をテストする

サードパーティ ログインなどを介してリモート サイトとやり取りするアプリでは、以下を行う必要があります。

新しい SameSite 動作にオプトインできるバージョンのクライアントを使って、Web アプリをテストします。 Chrome、Firefox、Chromium Edge のいずれにも、テストに使用できる新しいオプトイン機能フラグがあります。 アプリで SameSite のパッチを適用した後、古いクライアント バージョン (特に Safari) でテストします。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。

Chrome についてテストする

Chrome 78 以降では、一時的な軽減策が設定されているため、誤解を招く結果が得られます。 Chrome 78 以降の一時的な軽減策により、2 分より短い cookie が許可されます。 適切なテスト フラグが有効にされた Chrome 76 または 77 では、より正確な結果が提供されます。 SameSite の新しい動作をテストするには、chrome://flags/#same-site-by-default-cookiesEnabled に切り替えます。 古いバージョンの Chrome (75 以前) では、新しい設定 None を使うと失敗することが報告されています。 このドキュメントの「古いブラウザーのサポート」をご覧ください。

Google では、以前のバージョンの Chrome は提供されていません。 Chromium のダウンロードに関するページの手順に従って、古いバージョンの Chrome をテストしてください。 Chrome の古いバージョンを検索して示されるリンクからは、Chrome をダウンロードしないでください

Canary バージョン 80.0.3975.0 以降では、新しいフラグ --enable-features=SameSiteDefaultChecksMethodRigorously を使用してテストのために Lax+POST の一時的な軽減策を無効にし、軽減策が削除された機能の最終的な状態でサイトとサービスをテストすることができます。 詳しくは、The Chromium Projects の「SameSite Updates (SameSite の更新)」をご覧ください

Safari についてテストする

Safari 12 では以前のドラフトが厳密に実装されており、cookie が新しい値 None になっていると失敗します。 None は、このドキュメントの「古いブラウザーのサポート」のブラウザー検出コードを使用して回避されます。 MSAL、ADAL、または使用しているライブラリを使用して、Safari 12、Safari 13、WebKit ベースの OS スタイルのログインをテストします。 この問題は、基盤の OS バージョンによって変わります。 OSX Mojave (10.14) および iOS 12 には、SameSite の新しい動作との互換性の問題があることがわかっています。 OS を OSX Catalina (10.15) または iOS 13 にアップグレードすると、問題は解決します。 現在、Safari には新しい仕様の動作をテストするためのオプトイン フラグがありません。

Firefox についてテストする

Firefox による新しい標準のサポートは、バージョン 68 以降で、機能フラグ network.cookie.sameSite.laxByDefault を指定して about:config ページでオプトインすることでテストできます。 以前のバージョンの Firefox では、互換性の問題は報告されていません。

Edge ブラウザーについてテストする

Edge では、SameSite の古い標準がサポートされています。 Edge バージョン 44 には、新しい標準に関する既知の互換性の問題はありません。

Edge (Chromium) についてテストする

SameSite のフラグは、edge://flags/#same-site-by-default-cookies ページで設定されます。 Edge Chromium では互換性の問題は検出されませんでした。

Electron についてテストする

Electron の複数のバージョンには、Chromium の古いバージョンが含まれています。 たとえば、Teams で使用されている Electron のバージョンは Chromium 66 であり、以前の動作を示しています。 お使いの製品に使用されている Electron のバージョンを使って、互換性テストを実行する必要があります。 「古いブラウザーのサポート」をご覧ください。

その他の技術情報

サンプル ドキュメント
.NET Core Razor Pages ASP.NET Core 3.1 Razor Pages の SameSite cookie のサンプル

次のサンプルをダウンロードしてテストできます。

サンプル ドキュメント
.NET Core MVC ASP.NET Core 2.1 MVC の SameSite cookie のサンプル
.NET Core Razor Pages ASP.NET Core 2.1 Razor Pages の SameSite cookie のサンプル

12 月のパッチでの動作の変更

.NET Framework と .NET Core 2.1 での具体的な動作の変更は、SameSite プロパティによる None 値の解釈方法です。 値 None の意味は、パッチの前は "属性をまったく生成しない" で、パッチの後は "None という値で属性を生成する" です。 パッチの後では、SameSite の値が (SameSiteMode)(-1) の場合は、属性は生成されません。

フォーム認証とセッション状態の cookie に対する既定の SameSite 値 が、None から Lax に変更されました。

SameSite での API の使用

HttpContext.Response.Cookies.Append の既定値は Unspecified です。つまり、SameSite 属性は cookie に追加されず、クライアントでは既定の動作が使用されます (新しいブラウザーの場合は Lax、古いブラウザーの場合は None)。 次のコードでは、cookie の SameSite 値を SameSiteMode.Lax に変更する方法が示されています。

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

cookie を生成するすべての ASP.NET Core コンポーネントでは、シナリオに適した設定で、前の既定値をオーバーライドします。 オーバーライドされた前の既定値は変更されません。

コンポーネント cookie Default
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie Authentication CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

履歴と変更

SameSite のサポートは、2016 年のドラフト標準を使用する ASP.NET Core 2.0 で最初に実装されました。 2016 年の標準はオプトインでした。 ASP.NET Core は、複数の cookie を Lax に既定で設定することによりオプトインしました。 認証に関するいくつかのイシューが発生した後、SameSite のほとんどの使用は無効にされました

2019 年 11 月にパッチが発行されて、2016 標準から 2019 標準に更新されました。 SameSite 仕様の 2019 ドラフト:

  • 2016 ドラフトとの下位互換性はありません。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。
  • cookie を指定すると、既定で SameSite=Lax として扱われます。
  • クロスサイト配信を有効にするために明示的に SameSite=None アサートする cookie の指定は、Secure としてマークする必要があります。 None は、オプトアウトする新しいエントリです。
  • ASP.NET Core 2.1、2.2、3.0 に対して発行されたパッチによってサポートされます。 ASP.NET Core 3.1 には、SameSite の追加サポートがあります。
  • 2020 年 2 月に、Chrome によって既定で有効にされる予定です。 この標準へのブラウザーの移行は、2019 年に開始されました。

SameSite の 2016 ドラフト標準から 2019 ドラフト標準への変更によって影響を受ける API

古いブラウザーのサポート

2016 SameSite 標準では、不明な値を SameSite=Strict 値として扱う必要がありました。 2016 SameSite 標準をサポートする古いブラウザーからアクセスされるアプリは、取得した SameSite プロパティの値が None である場合、機能しなくなる可能性があります。 Web アプリで古いブラウザーをサポートする場合は、ブラウザーの検出を実装する必要があります。 User-Agents の値は揮発性が高く、頻繁に変更されるため、ASP.NET Core にはブラウザーの検出は実装されていません。 Microsoft.AspNetCore.CookiePolicy の拡張ポイントを使用すると、User-Agent 固有のロジックを接続できます。

Startup.Configure では、UseAuthentication または cookie を書き込む "すべての" メソッドを呼び出す前に、UseCookiePolicy を呼び出すコードを追加します。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Startup.ConfigureServices では、次のようなコードを追加します。

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.MinimumSameSitePolicy = (SameSiteMode)(-1);
        options.OnAppendCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });

    services.AddRazorPages();
}

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = (SameSiteMode)(-1);
        }

    }
}

前のサンプルの MyUserAgentDetectionLib.DisallowsSameSiteNone は、ユーザー エージェントで SameSite None がサポートされていないかどうかを検出する、ユーザー提供のライブラリです。

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

次のコードは、DisallowsSameSiteNone メソッドのサンプルです。

警告

次のコードは、デモのみを目的としたものです。

  • 完全なものと考えないでください。
  • メンテナンスもサポートもされません。
public static bool DisallowsSameSiteNone(string userAgent)
{
    // Check if a null or empty string has been passed in, since this
    // will cause further interrogation of the useragent to fail.
     if (String.IsNullOrWhiteSpace(userAgent))
        return false;
    
    // Cover all iOS based browsers here. This includes:
    // - Safari on iOS 12 for iPhone, iPod Touch, iPad
    // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
    // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
    // All of which are broken by SameSite=None, because they use the iOS networking
    // stack.
    if (userAgent.Contains("CPU iPhone OS 12") ||
        userAgent.Contains("iPad; CPU OS 12"))
    {
        return true;
    }

    // Cover Mac OS X based browsers that use the Mac OS networking stack. 
    // This includes:
    // - Safari on Mac OS X.
    // This does not include:
    // - Chrome on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
        userAgent.Contains("Version/") && userAgent.Contains("Safari"))
    {
        return true;
    }

    // Cover Chrome 50-69, because some versions are broken by SameSite=None, 
    // and none in this range require it.
    // Note: this covers some pre-Chromium Edge versions, 
    // but pre-Chromium Edge does not require SameSite=None.
    if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
    {
        return true;
    }

    return false;
}

アプリで SameSite の問題をテストする

サードパーティ ログインなどを介してリモート サイトとやり取りするアプリでは、以下を行う必要があります。

新しい SameSite 動作にオプトインできるバージョンのクライアントを使って、Web アプリをテストします。 Chrome、Firefox、Chromium Edge のいずれにも、テストに使用できる新しいオプトイン機能フラグがあります。 アプリで SameSite のパッチを適用した後、古いクライアント バージョン (特に Safari) でテストします。 詳しくは、このドキュメントの「古いブラウザーのサポート」をご覧ください。

Chrome についてテストする

Chrome 78 以降では、一時的な軽減策が設定されているため、誤解を招く結果が得られます。 Chrome 78 以降の一時的な軽減策により、2 分より短い cookie が許可されます。 適切なテスト フラグが有効にされた Chrome 76 または 77 では、より正確な結果が提供されます。 SameSite の新しい動作をテストするには、chrome://flags/#same-site-by-default-cookiesEnabled に切り替えます。 古いバージョンの Chrome (75 以前) では、新しい設定 None を使うと失敗することが報告されています。 このドキュメントの「古いブラウザーのサポート」をご覧ください。

Google では、以前のバージョンの Chrome は提供されていません。 Chromium のダウンロードに関するページの手順に従って、古いバージョンの Chrome をテストしてください。 Chrome の古いバージョンを検索して示されるリンクからは、Chrome をダウンロードしないでください

Canary バージョン 80.0.3975.0 以降では、新しいフラグ --enable-features=SameSiteDefaultChecksMethodRigorously を使用してテストのために Lax+POST の一時的な軽減策を無効にし、軽減策が削除された機能の最終的な状態でサイトとサービスをテストすることができます。 詳しくは、The Chromium Projects の「SameSite Updates (SameSite の更新)」をご覧ください

Safari についてテストする

Safari 12 では以前のドラフトが厳密に実装されており、cookie が新しい値 None になっていると失敗します。 None は、このドキュメントの「古いブラウザーのサポート」のブラウザー検出コードを使用して回避されます。 MSAL、ADAL、または使用しているライブラリを使用して、Safari 12、Safari 13、WebKit ベースの OS スタイルのログインをテストします。 この問題は、基盤の OS バージョンによって変わります。 OSX Mojave (10.14) および iOS 12 には、SameSite の新しい動作との互換性の問題があることがわかっています。 OS を OSX Catalina (10.15) または iOS 13 にアップグレードすると、問題は解決します。 現在、Safari には新しい仕様の動作をテストするためのオプトイン フラグがありません。

Firefox についてテストする

Firefox による新しい標準のサポートは、バージョン 68 以降で、機能フラグ network.cookie.sameSite.laxByDefault を指定して about:config ページでオプトインすることでテストできます。 以前のバージョンの Firefox では、互換性の問題は報告されていません。

Edge ブラウザーについてテストする

Edge では、SameSite の古い標準がサポートされています。 Edge バージョン 44 には、新しい標準に関する既知の互換性の問題はありません。

Edge (Chromium) についてテストする

SameSite のフラグは、edge://flags/#same-site-by-default-cookies ページで設定されます。 Edge Chromium では互換性の問題は検出されませんでした。

Electron についてテストする

Electron の複数のバージョンには、Chromium の古いバージョンが含まれています。 たとえば、Teams で使用されている Electron のバージョンは Chromium 66 であり、以前の動作を示しています。 お使いの製品に使用されている Electron のバージョンを使って、互換性テストを実行する必要があります。 「古いブラウザーのサポート」をご覧ください。

その他の技術情報

サンプル ドキュメント
.NET Core MVC ASP.NET Core 2.1 MVC の SameSite cookie のサンプル
.NET Core Razor Pages ASP.NET Core 2.1 Razor Pages の SameSite cookie のサンプル