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.Web
HttpCookie
クラスと直接やり取りします。
SystemWebCookieManager
は、SameSite
サポートを有効にするために.NET 4.7.2 System.Web
API に依存し、動作を変更するためにパッチに依存します。
SystemWebCookieManager
を使用する理由については、OWIN と System.Web 応答 Cookie の統合に関する問題に関するページで説明されています。 System.Web
で実行する場合は、SystemWebCookieManager
をお勧めします。
次のコードは、SameSite
を Lax
に設定します。
owinContext.Response.Cookies.Append("My Key", "My Value", new CookieOptions()
{
SameSite = SameSiteMode.Lax
});
次の API では、SameSite
が使用されます。
- Microsoft.Owin.SameSiteMode
- CookieOptions.SameSite
- CookieAuthenticationOptions クラス
- CookieAuthenticationOptions.CookieSameSite
- ICookieManager
- SystemWebCookieManager
- SystemWebChunkingCookieManager
- CookieAuthenticationOptions.CookieManager
- OpenIdConnectAuthenticationOptions.CookieManager
履歴と変更
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;
}
}
上記のサンプルでは、DisallowsSameSiteNone
が CheckSameSite
メソッドで呼び出されています。 DisallowsSameSiteNone
は、ユーザー エージェントが SameSite
None
をサポートしていないかどうかを検出するユーザー メソッドです。
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-cookies
を Enabled に切り替えます。 古いバージョンの 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 のバージョンとの互換性テストを独自に実行する必要があります。 「古いブラウザーのサポート」をご覧ください。