SameSite Cookie 和 .NET (OWIN)

作者:Rick Anderson

SameSite 是一个 IETF 草案,旨在针对跨站点请求伪造提供一些保护, (CSRF) 攻击。 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 中使用 SameSite Cookie

使用 SameSite 的 API 用法

Microsoft.OwinSameSite有自己的实现:

  • 这不直接依赖于 中的 System.Web
  • SameSite 适用于包面向 Microsoft.Owin 的所有版本,即 .NET 4.5 及更高版本。
  • 只有 SystemWebCookieManager 组件直接与 类交互 System.WebHttpCookie

SystemWebCookieManager 依赖于 .NET 4.7.2 System.Web API 来启用 SameSite 支持,以及修补程序来更改行为。

OWIN 和 System.Web 响应 Cookie 集成问题中概述了使用SystemWebCookieManager原因。 SystemWebCookieManager 在 上 System.Web运行时,建议使用 。

以下代码将 设置为 SameSiteLax

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

以下 API 使用 SameSite

历史记录和更改

Microsoft.Owin 从未支持 SameSite 2016 年草案标准

对 SameSite 2019 草稿的支持仅在 4.1.0 及更高版本中可用Microsoft.Owin。 以前版本没有修补程序。

规范的 2019 年草案 SameSite

  • 不与 2016 草案向后兼容。 有关详细信息,请参阅本文档中的支持较旧浏览器
  • 指定默认情况下将 Cookie 视为 SameSite=Lax
  • 指定显式断言 SameSite=None 以启用跨站点传递的 Cookie 应标记为 SecureNone 是要选择退出的新项。
  • 计划在 2020 年 2 月Chrome 默认启用。 浏览器已开始在 2019 年迁移到此标准。
  • 受发布修补程序的支持,如知识库文章中所述。 有关详细信息,请参阅在 .NET Framework 中支持 SameSite 的知识库文章

支持较旧浏览器

2016 SameSite 年标准要求必须将未知值视为 SameSite=Strict 值。 从支持 2016 SameSite 标准的旧浏览器访问的应用在获取 SameSite 值为 None的属性时可能会中断。 如果 Web 应用打算支持较旧浏览器,则必须实现浏览器检测。 ASP.NET 不实现浏览器检测,因为User-Agents值高度可变且经常更改。 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 是一种用户方法,用于检测用户代理是否不支持 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+ 临时缓解允许使用不到两分钟的 Cookie。 启用合适的测试标志后,Chrome 76 或 77 会提供更准确的结果。 若要测试新SameSite行为,请切换到chrome://flags/#same-site-by-default-cookies“已启用”。 据报告,较旧版本的 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 行为的兼容性问题。 将操作系统升级到 OSX Catalina (10.15) 或 iOS 13 会解决此问题。 Safari 当前没有用于测试新规范行为的选择加入标志。

使用 Firefox 测试

通过在具有功能标志 network.cookie.sameSite.laxByDefaultabout:config 页面上选择加入,可在版本 68+ 上测试 Firefox 对新标准的支持。 较旧版本的 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 版本执行自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器

其他资源