ASP.NET で SameSite Cookie を操作する

作成者: Rick Anderson

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

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

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

Cookie を出力する各 ASP.NET コンポーネントは、SameSite が適切かどうかを判断する必要があります。

2019 .Net SameSite 更新プログラムをインストールした後のアプリケーションに関する問題の 既知の問題 を参照してください。

ASP.NET 4.7.2 および 4.8 での SameSite の使用

.Net 4.7.2 および 4.8 では、 2019 年 12 月の更新プログラムのリリース以降、SameSite の 2019 ドラフト標準がサポートされています。 開発者は、 HttpCookie.SameSite プロパティを使用して SameSite ヘッダーの値をプログラムで制御できます。 SameSite プロパティをStrictLax、またはNoneに設定すると、これらの値が Cookie を使用してネットワーク上に書き込まれます。 (SameSiteMode)(-1) に設定することは、Cookie と一緒にネットワークに SameSite ヘッダーを含めないことを示します。 構成ファイルの HttpCookie.Secure プロパティ ('requireSSL' ) を使用して、Cookie を Secure としてマークすることができます。

新しい HttpCookie インスタンスは、既定で SameSite=(SameSiteMode)(-1) および Secure=falseされます。 これらの既定値は、 system.web/httpCookies 構成セクションでオーバーライドできます。文字列 "Unspecified" は、 (SameSiteMode)(-1)のわかりやすい構成専用構文です。

<configuration>
 <system.web>
  <httpCookies sameSite="[Strict|Lax|None|Unspecified]" requireSSL="[true|false]" />
 <system.web>
<configuration>

また、ASP.Net は、匿名認証、フォーム認証、セッション状態、ロール管理という 4 つの機能に固有の Cookie を発行します。 実行時に取得されたこれらの Cookie のインスタンスは、他の HttpCookie インスタンスと同様に、 SameSite プロパティと Secure プロパティを使用して操作できます。 ただし、SameSite 標準のパッチワークの出現により、これら 4 つの機能 Cookie の構成オプションに一貫性がありません。 関連する構成セクションと属性 (既定値) を次に示します。 フィーチャーに SameSite または Secure 関連属性がない場合、この機能は前述の system.web/httpCookies セクションで構成された既定値にフォールバックします。

<configuration>
 <system.web>
  <anonymousIdentification cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
  <authentication>
   <forms cookieSameSite="Lax" requireSSL="false" />
  </authentication>
  <sessionState cookieSameSite="Lax" /> <!-- No config attribute for Secure -->
  <roleManager cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
 <system.web>
<configuration>

: 'Unspecified' は、現時点では system.web/httpCookies@sameSite でのみ使用できます。 今後の更新では、前に示した cookieSameSite 属性に同様の構文を追加したいと考えています。 コードでの (SameSiteMode)(-1) の設定は、これらの Cookie のインスタンスでも機能します。*

英語以外の言語でこれを読んでいる場合は、ネイティブ言語でコード コメントを表示する場合は、この GitHub ディスカッションの問題 でお知らせください。

.NET アプリの再ターゲット

.NET 4.7.2 以降をターゲットにするには:

  • web.config に次のものが含まれていることを確認します。

    <system.web>
      <compilation targetFramework="4.7.2"/>
      <httpRuntime targetFramework="4.7.2"/>
    </system.web>
    
    
  • プロジェクト ファイルに正しい TargetFrameworkVersion が含まれていることを確認します。

    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    

    .NET 移行ガイドには、詳細が記載されています。

  • プロジェクト内の NuGet パッケージが正しいフレームワーク バージョンを対象としていることを確認します。 packages.config ファイルを調べることで、正しいフレームワークのバージョンを確認できます。次に例を示します。

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net472" />
      <package id="Microsoft.ApplicationInsights" version="2.4.0" targetFramework="net451" />
    </packages>
    

    上記の packages.config ファイルでは、 Microsoft.ApplicationInsights パッケージは次のようになります。

    • .NET 4.5.1 を対象とします。
    • フレームワーク ターゲットを対象とする更新されたパッケージが存在する場合は、その targetFramework 属性を net472 に更新する必要があります。

4.7.2 より前の .NET バージョン

Microsoft では、同じサイト Cookie 属性を記述するために、その 4.7.2 より前のバージョンの .NET はサポートされていません。 信頼性のある手段が見つかりませんでした。

  • ブラウザーのバージョンに基づいて属性が正しく記述されていることを確認します。
  • 以前のバージョンのフレームワークで認証 Cookie とセッション Cookie をインターセプトして調整します。

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

.NET Framework の具体的な動作の変更は、 SameSite プロパティが None 値を解釈する方法です。

  • パッチの前に、 None の値は次の意味を持つものです。
    • 属性をまったく出力しないでください。
  • パッチの後:
    • Noneの値は、"None の値を持つ属性を出力する" を意味します。
    • SameSite(SameSiteMode)(-1)値を指定すると、属性は出力されません。

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

ブラウザーに対する変更の影響の概要

パッチをインストールし、 SameSite.Noneで Cookie を発行すると、次の 2 つのいずれかが発生します。

  • Chrome v80 は、新しい実装に従ってこの Cookie を処理し、Cookie に同じサイト制限を適用しません。
  • 新しい実装をサポートするように更新されていないブラウザーは、古い実装に従います。 古い実装では、次のように表示されます。
    • 理解できない値が表示された場合は、無視して、厳密な同じサイト制限に切り替えます。

そのため、アプリが Chrome で中断されるか、他の多数の場所で中断されます。

履歴と変更

SameSite のサポートは、 2016 ドラフト標準を使用して .NET 4.7.2 で最初に実装されました。

Windows 用の 2019 年 11 月 19 日の更新プログラムは、2016 年の標準から 2019 年の標準に .NET 4.7.2 以降を更新しました。 他のバージョンの Windows では、追加の更新プログラムが予定されています。 詳細については、SameSite を .NET Framework でサポートする KB 記事を参照してください。

SameSite 仕様の 2019 ドラフト:

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

既知の問題

2016 年と 2019 年のドラフト仕様には互換性がないため、2019 年 11 月の .Net Framework 更新プログラムでは、いくつかの変更が発生し、互換性が損なわれる可能性があります。

  • セッション状態とフォーム認証の Cookie は、未指定ではなく Lax としてネットワークに書き込まれるようになりました。
    • ほとんどのアプリは SameSite=Lax Cookie で動作しますが、 iframe を利用するサイトまたはアプリケーション間で POST するアプリでは、セッション状態またはフォーム承認 Cookie が想定どおりに使用されていない場合があります。 これを解決するには、前に説明したように、適切な構成セクションの cookieSameSite 値を変更します。
  • コードまたは構成で SameSite=None を明示的に設定する HttpCookie は、Cookie で書き込まれた値を持つようになりましたが、以前は省略されています。 これにより、2016 ドラフト標準のみをサポートする古いブラウザーで問題が発生する可能性があります。
    • 2019年のドラフト標準をサポートするブラウザをSameSite=None Cookieでターゲットにする場合は、Secureとしてマークすることを忘れないでください。認識されない可能性があります。
    • SameSite=Noneを書き込まないという 2016 年の動作に戻すには、アプリ設定aspnet:SupressSameSiteNone=trueを使用します。 これは、アプリ内のすべての HttpCookie に適用されることに注意してください。

.Net 4.7.2 アプリで Azure App Service が SameSite の動作を構成する方法については、Azure App Service の SameSite Cookie 処理と .NET Framework 4.7.2 パッチを参照してください。

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

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

問題を解決するための Microsoft のアプローチは、ブラウザーが cookie をサポートしていないことがわかっている場合に、ブラウザー検出コンポーネントを実装して cookie から sameSite=None 属性を削除できるように支援することです。 Googleのアドバイスは、新しい属性を持つクッキーと属性をまったく持たないクッキーを発行することでした。 しかし、Googleのアドバイスは限られていると考えられます。 一部のブラウザー、特にモバイル ブラウザーでは、サイトまたはドメイン名が送信できる Cookie の数に非常に小さな制限があります。 複数の Cookie 、特に認証 Cookie などの大規模な Cookie を送信すると、モバイル ブラウザーの制限に非常に迅速に達する可能性があり、診断と修正が困難なアプリエラーが発生する可能性があります。 さらに、フレームワークとして、二重クッキーアプローチを使用するように更新されない可能性があるサードパーティのコードとコンポーネントの大規模なエコシステムがあります。

この GitHub リポジトリのサンプル プロジェクトで使用されるブラウザー検出コードは、2 つのファイルに含まれています

これらの検出は、2016 標準をサポートし、属性を完全に削除する必要がある、最も一般的なブラウザー エージェントです。 これは完全な実装ではありません。

  • テスト サイトに表示されないブラウザーがアプリに表示される場合があります。
  • 環境に必要に応じて検出を追加する準備が整う必要があります。

検出の接続方法は、使用している .NET と Web フレームワークのバージョンによって異なります。 HttpCookie 呼び出しサイトで次のコードを呼び出すことができます。

private void CheckSameSite(HttpContext httpContext, HttpCookie cookie)
{
    if (cookie.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.UserAgent;
        if (BrowserDetection.DisallowsSameSiteNone(userAgent))
        {
            cookie.SameSite = (SameSiteMode)(-1);
        }
    }
}

次の ASP.NET 4.7.2 SameSite Cookie のトピックを参照してください。

サイトが HTTPS にリダイレクトされるようにする

ASP.NET 4.x、WebForms、MVC では、 IIS の URL 書き換え 機能を使用して、すべての要求を HTTPS にリダイレクトできます。 次の XML は、サンプル ルールを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Redirect to https" stopProcessing="true">
          <match url="(.*)"/>
          <conditions>
            <add input="{HTTPS}" pattern="Off"/>
            <add input="{REQUEST_METHOD}" pattern="^get$|^head$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

IIS URL 書き換え のオンプレミス インストールでは、インストールが必要になる場合があるオプションの機能です。

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

サポートしているブラウザーでアプリをテストし、Cookie を含むシナリオを実行する必要があります。 クッキーに関するシナリオは通常、

  • ログイン フォーム
  • Facebook、Azure AD、OAuth、OIDC などの外部ログイン メカニズム
  • 他のサイトからの要求を受け入れるページ
  • iframe に埋め込まれるよう設計されたアプリ内のページ

Cookie がアプリで正しく作成、永続化、削除されていることを確認する必要があります。

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

新しい 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 の更新)」をご覧ください

Chrome 80 以降でテストする

新しい属性をサポートする Chrome のバージョンをダウンロードします。 執筆時点では、現在のバージョンは Chrome 80 です。 Chrome 80 では、新しい動作を使用するためにフラグ chrome://flags/#same-site-by-default-cookies 有効にする必要があります。 また、(chrome://flags/#cookies-without-same-site-must-be-secure) を有効にして、同じSite 属性が有効になっていない Cookie の今後の動作をテストする必要があります。 Chrome 80 は、属性のない Cookie を SameSite=Laxとして扱うように変更する予定です。ただし、特定のリクエストに対しては猶予期間が設けられます。 時間指定された猶予期間を無効にするには、次のコマンド ライン引数を使用して Chrome 80 を起動できます。

--enable-features=SameSiteDefaultChecksMethodRigorously

Chrome 80 には、同じサイト属性が見つからないという警告メッセージがブラウザー コンソールに表示されます。 F12 キーを使用してブラウザー コンソールを開きます。

Safari についてテストする

Safari 12 では、以前のドラフトが厳密に実装されており、新しい None 値が Cookie 内にある場合は失敗します。 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 以降で、機能フラグ about:config を指定して network.cookie.sameSite.laxByDefault ページでオプトインすることでテストできます。 以前のバージョンの 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 のバージョンとの互換性テストを独自に実行する必要があります。 古いブラウザーのサポートを参照してください。

SameSite パッチをリバートする

.NET Framework アプリで更新された sameSite の動作を以前の動作に戻すことができます。この動作では、 Noneの値に対して同じSite 属性が出力されず、認証とセッション Cookie が値を出力しないように戻すことができます。 Chrome の変更により、標準への変更をサポートするブラウザーを使用するユーザーの外部 POST 要求または認証が中断されるため、これは 非常に一時的な修正プログラムと見なす必要があります。

.NET 4.7.2 の動作を元に戻す

web.config を更新して、次の構成設定を含めます。

<configuration> 
  <appSettings>
    <add key="aspnet:SuppressSameSiteNone" value="true" />
  </appSettings>
 
  <system.web> 
    <authentication> 
      <forms cookieSameSite="None" /> 
    </authentication> 
    <sessionState cookieSameSite="None" /> 
  </system.web> 
</configuration>

その他のリソース