通过


在 ASP.NET 中使用 SameSite Cookie

作者:Rick Anderson

SameSite 是一种 IETF 草案标准,旨在针对跨网站请求伪造 (CSRF) 攻击提供一些保护。 最初于 2016 年起草,草案标准于 2019 年更新。 更新后的标准与以前的标准不向后兼容,以下是最明显的差异:

  • 默认情况下,没有 SameSite 标头的 Cookie 被视为 SameSite=Lax
  • SameSite=None 必须用于允许跨站点使用cookies。
  • 断言 SameSite=None 的 Cookie 也必须标记为 Secure
  • 使用 <iframe> 的应用程序可能会遇到与 sameSite=LaxsameSite=Strict Cookie 有关的问题,因为 <iframe> 被视为跨站点方案。
  • SameSite=None的值不符合2016标准,因此导致某些实现将此类 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

自 2019 年 12 月发布更新以来,.Net 4.7.2 和 4.8 支持 SameSite 的 2019 年草稿标准草案 。 开发人员可以使用 HttpCookie.SameSite 属性以编程方式控制 SameSite 标头的值。 将 SameSite 属性设置为 StrictLaxNone 会导致这些值连同 Cookie 一同写入网络。 将其设置为等于 (SameSiteMode)(-1) 指示不应在包含 Cookie 的网络上包含 SameSite 标头。 HttpCookie.Secure 属性或配置文件中的“requireSSL”可用于将 Cookie 标记为安全或不安全。

新的 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 还针对这些功能发布了四个特定的 Cookie:匿名身份验证、表单身份验证、会话状态和角色管理。 可以像操作任何其他 HttpCookie 实例一样,使用SameSiteSecure属性来操作在运行时获取的这些 Cookie 实例。 但是,由于 SameSite 标准逐渐出现,这四种功能 Cookie 的配置选项不一致。 下面显示了相关的配置部分和属性以及默认值。 如果某个功能没有 SameSiteSecure 相关的属性,则该功能将回退到上面讨论的部分中配置的 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>

注意:“未指定”目前仅可供 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不支持低于 4.7.2 的 .NET 版本来编写同一站点 Cookie 属性。 我们没有找到一种可靠的方法:

  • 确保根据浏览器版本正确编写属性。
  • 截获和调整旧框架版本上的身份验证和会话 Cookie。

12 月补丁行为变更

在 .NET Framework 中,特定的行为更改是SameSite属性对None值的解释方式:

  • 在修补之前,None 的值表示:
    • 不要输出该属性。
  • 修补后:
    • None值为“发出值为None”的属性。
    • (SameSiteMode)(-1) 的值导致 SameSite 属性不发出。

窗体身份验证和会话状态 Cookie 的默认 SameSite 值已从 None 更改为 Lax

对浏览器的更改影响摘要

如果安装修补程序并发送带有SameSite.None的 Cookie,可能会发生以下两种情况之一:

  • Chrome v80 将根据新实现处理此 Cookie,并且不会对 Cookie 强制实施相同的站点限制。
  • 任何尚未更新以支持新实现的浏览器都将遵循旧实现。 旧的实现说:
    • 如果看到不了解的值,请忽略该值并切换到严格的相同站点限制。

因此,要么应用在 Chrome 中中断,要么在许多其他位置中断。

历史记录和更改

SameSite 支持首先在 .NET 4.7.2 中使用 2016 草稿标准实现。

2019 年 11 月 19 日 Windows 更新了从 2016 年标准升级到 2019 标准版的 .NET 4.7.2+ 。 其他版本的 Windows 即将推出其他更新。 有关详细信息,请参阅 支持 .NET Framework 中的 SameSite 的知识库文章

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 的应用程序发布的应用可能会发现其会话状态或表单授权 Cookie 未按预期使用。 若要解决此问题,请更改 cookieSameSite 前面所述的相应配置部分中的值。
  • 在代码或配置中显式设置的 SameSite=None 的 HttpCookies,现在这个值会随 Cookie 一起写入,而之前这个值是被忽略的。 这可能会导致仅支持 2016 草稿标准的较旧浏览器出现问题。
    • 面向支持2019年草稿标准的浏览器使用SameSite=None Cookie时,请记住也要标记为Secure,否则可能无法识别。
    • 要恢复到 2016 年的行为,即不写入 SameSite=None,请使用应用设置 aspnet:SupressSameSiteNone=true。 请注意,这适用于应用中的所有 HttpCookies。

有关 Azure 应用服务在 .NET 4.7.2 应用中配置 SameSite 行为的信息,请参阅 Azure 应用服务——SameSite Cookie 处理和 .NET Framework 4.7.2 修补程序

支持较旧浏览器

2016 SameSite 标准要求必须将未知值视为 SameSite=Strict 值。 从支持 2016 SameSite 标准的较旧浏览器访问的应用在遇到值为 None 的 SameSite 属性时可能会中断。 如果 Web 应用打算支持较旧浏览器,则必须实现浏览器检测。 ASP.NET 不实现浏览器检测,因为 User-Agents 值非常易失,并且经常更改。

Microsoft解决此问题的方法是,如果已知浏览器不支持它,则帮助你实现浏览器检测组件,以从 Cookie 中去除 sameSite=None 属性。 谷歌的建议是发出双重 Cookie,一个具有新属性,一个没有属性。 然而,我们认为谷歌的建议有限。 某些浏览器(尤其是移动浏览器)对站点或域名可以发送的 Cookie 数量有非常小的限制。 发送多个 Cookie,尤其是身份验证 Cookie 等大型 Cookie 可以快速达到移动浏览器限制,导致难以诊断和修复的应用故障。 此外,作为一个框架,第三方代码和组件的大型生态系统可能无法更新为使用双重 Cookie 方法。

此 GitHub 存储库示例项目中使用的浏览器检测代码包含在两个文件中

这些检测是我们所看到的支持 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 的场景。 Cookie 场景通常涉及

  • 登录表单
  • 外部登录机制,例如 Facebook、Azure AD、OAuth 和 OIDC
  • 接受来自其他网站的请求的页面
  • 应用程序中设计用于嵌入 iframe 的页面

应检查是否已在应用中正确创建、保存和删除 Cookie。

与远程站点交互(例如通过第三方登录)的应用需要:

使用可选择加入新 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。

从 Canary 版本 80.0.3975.0 开始,可以使用新标志 --enable-features=SameSiteDefaultChecksMethodRigorously 为进行测试而禁用 Lax+POST 临时缓解,以便允许在删除了缓解的功能的最终状态下测试站点和服务。 有关详细信息,请参阅 The Chromium Projects 项目中的SameSite 更新

使用 Chrome 80+ 进行测试

下载 支持其新属性的 Chrome 版本。 撰写本文时,当前版本为 Chrome 80。 Chrome 80 需要启用标志 chrome://flags/#same-site-by-default-cookies 才能使用新行为。 还应启用(chrome://flags/#cookies-without-same-site-must-be-secure)来测试未启用 SameSite 属性的 Cookie 将如何表现。 Chrome 80 计划切换,将不带属性的 Cookie 视为 SameSite=Lax,尽管某些请求有一个有限的宽限期。 若要禁用计时宽限期 Chrome 80,可以使用以下命令行参数启动:

--enable-features=SameSiteDefaultChecksMethodRigorously

Chrome 80 在浏览器控制台中出现有关缺少 sameSite 属性的警告消息。 使用 F12 打开浏览器控制台。

使用 Safari 测试

Safari 12 严格执行以前的草稿,当新 None 值位于 Cookie 中时失败。 通过本文档中的浏览器检测代码None,可以避免与支持较旧浏览器相关的问题。 可使用 MSAL、ADAL 或所使用的任何库来测试 Safari 12、Safari 13 和基于 WebKit 的操作系统样式登录。 问题取决于基础 OS 版本。 已知 OSX Mojave (10.14) 和 iOS 12 存在与新 SameSite 行为相关的兼容性问题。 将操作系统升级到 OSX Catalina (10.15) 或 iOS 13 会解决此问题。 Safari 当前没有用于测试新规范行为的试用标记。

使用 Firefox 测试

通过在 network.cookie.sameSite.laxByDefault 页面上启用功能开关 about: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 版本自行执行兼容性测试。 请参阅 支持较旧的浏览器

还原 SameSite 补丁

可以将 .NET Framework 应用中更新的 SameSite 行为还原为其原来的状态,其中对于 None 值不发出 SameSite 属性,并且将身份验证和会话 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>

其他资源