Share via


SameSite Cookie と Open Web Interface for .NET (OWIN)

作成者: Rick Anderson

SameSite は、クロスサイト リクエスト フォージェリ (CSRF) 攻撃からのある程度の保護を提供するように設計された IETF ドラフトです。 SameSite 2019 ドラフトでは:

  • 既定で Cookie が SameSite=Lax として扱われます。
  • クロスサイト配信を有効にするために SameSite=None を明示的にアサートする Cookie を Secure としてマークする必要があると述べられています。

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

None パラメータを使うと、2016 年以前のドラフト標準 (iOS 12 など) を実装したクライアントとの互換性の問題が発生します。 このドキュメントの「古いブラウザーのサポート」をご覧ください。

Cookie を発行する各 OWIN コンポーネントで、SameSite が適切かどうかを判断する必要があります。

この記事の ASP.NET 4.x バージョンについては、「ASP.NET Core での SameSite Cookie の使用」を参照してください。

SameSite での API の使用

Microsoft.Owin には独自の SameSite 実装があります。

  • これは System.Web のものに直接依存していません。
  • SameSite は、Microsoft.Owin パッケージの対象となる .NET 4.5 以降のすべてのバージョンで動作します。
  • SystemWebCookieManager コンポーネントのみが System.WebHttpCookie クラスと直接やり取りします。

SystemWebCookieManager は、SameSite サポートを有効にするために.NET 4.7.2 System.Web API に依存し、動作を変更するためにパッチに依存します。

SystemWebCookieManager を使用する理由については、OWIN と System.Web 応答 Cookie の統合に関する問題に関するページで説明されています。 System.Web で実行する場合は、SystemWebCookieManager をお勧めします。

次のコードは、SameSiteLax に設定します。

owinContext.Response.Cookies.Append("My Key", "My Value", new CookieOptions()
{
    SameSite = SameSiteMode.Lax
});

次の API では、SameSite が使用されます。

履歴と変更

Microsoft.Owin では、SameSite 2016 ドラフト標準はサポートされていませんでした。

SameSite 2019 ドラフトのサポートは、Microsoft.Owin 4.1.0 以降でのみ利用できます。 以前のバージョンのパッチはありません。

SameSite の 2019 ドラフトの仕様では:

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

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

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

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

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseOpenIdConnectAuthentication(
             new OpenIdConnectAuthenticationOptions
             {
                 // … Your preexisting options … 
                 CookieManager = new SameSiteCookieManager(
                                     new SystemWebCookieManager())
             });

        // Remaining code removed for brevity.

上記のコードには、.NET 4.7.2 以降の SameSite パッチが必要です。

次のコードは、SameSiteCookieManager の実装例を示しています。

public class SameSiteCookieManager : ICookieManager
{
    private readonly ICookieManager _innerManager;

    public SameSiteCookieManager() : this(new CookieManager())
    {
    }

    public SameSiteCookieManager(ICookieManager innerManager)
    {
        _innerManager = innerManager;
    }

    public void AppendResponseCookie(IOwinContext context, string key, string value,
                                     CookieOptions options)
    {
        CheckSameSite(context, options);
        _innerManager.AppendResponseCookie(context, key, value, options);
    }

    public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
    {
        CheckSameSite(context, options);
        _innerManager.DeleteCookie(context, key, options);
    }

    public string GetRequestCookie(IOwinContext context, string key)
    {
        return _innerManager.GetRequestCookie(context, key);
    }

    private void CheckSameSite(IOwinContext context, CookieOptions options)
    {
        if (options.SameSite == Microsoft.Owin.SameSiteMode.None 
                             && DisallowsSameSiteNone(context))
        {
            options.SameSite = null;
        }
    }

上記のサンプルでは、DisallowsSameSiteNoneCheckSameSite メソッドで呼び出されています。 DisallowsSameSiteNone は、ユーザー エージェントが SameSiteNone をサポートしていないかどうかを検出するユーザー メソッドです。

private void CheckSameSite(IOwinContext context, CookieOptions options)
{
    if (options.SameSite == Microsoft.Owin.SameSiteMode.None 
                         && DisallowsSameSiteNone(context))
    {
        options.SameSite = null;
    }
}

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

警告

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

  • 完全なものと考えないでください。
  • メンテナンスもサポートもされません。
public static bool DisallowsSameSiteNone(IOwinContext context)
{
    var userAgent = context.Request.Headers["User-Agent"];
    
    if (string.IsNullOrEmpty(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 をダウンロードしないでください

Safari についてテストする

Safari 12 では以前のドラフトが厳密に実装されており、新しい None 値が Cookie 内にある場合は失敗します。 None は、このドキュメントの「古いブラウザーのサポート」のブラウザー検出コードを使用して回避されます。 MSAL または使用しているライブラリを使用して、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 のバージョンとの互換性テストを独自に実行する必要があります。 「古いブラウザーのサポート」をご覧ください。

その他のリソース