- 通用
- 身份验证
- 授权
- 数据保护
- 机密管理
在 ASP.NET Core 中使用 SameSite cookie
SameSite是一种IETF草案,旨在针对跨站点请求伪造(CSRF)攻击提供某些防护。 SameSite 2019 草案:
- 默认情况下,将 cookie 视为
SameSite=Lax
。 - 为了启用跨站点传递而显式断言
SameSite=None
的 cookie 应标记为Secure
。
Lax
适用于大多数应用 cookie。 某些形式的身份验证,例如OpenID connect (OIDC)和ws-federation默认为基于 POST 的重定向。 基于后期的重定向会触发 SameSite 浏览器保护,因此,对这些组件禁用了 SameSite。 由于请求的流动方式不同,大多数OAuth登录名不受影响。
None
参数会导致实现之前2016草案标准(例如,iOS 12)的客户端的兼容性问题。 请参阅本文档中的支持旧版浏览器。
发出 cookie 的每个 ASP.NET Core 组件都需要确定 SameSite 是否合适。
使用 SameSite 的 API 用法
Httpcontext.current默认值为 Unspecified
,这意味着,不会向 cookie 中添加 SameSite 属性,客户端将使用其默认行为(对于新浏览器而言,对于旧浏览器则为 "无")。 下面的代码演示如何将 cookie SameSite 值更改为 SameSiteMode.Lax
:
HttpContext.Response.Cookies.Append( "name", "value", new CookieOptions() { SameSite = SameSiteMode.Lax });
发出 cookie 的所有 ASP.NET Core 组件都用适用于其方案的设置替代前面的默认值。 先前重写的默认值尚未更改。
ASP.NET Core 3.1 和更高版本提供了以下 SameSite 支持:
- 重新定义发出
SameSiteMode.None
的行为SameSite=None
- 将新值添加
SameSiteMode.Unspecified
以省略 SameSite 属性。 - 所有 cookie Api 默认为
Unspecified
。 某些使用 cookie 的组件会将值设置得更加特定于其应用场景。 有关示例,请参阅上表。
在 ASP.NET Core 3.0 及更高版本中,SameSite 默认值已更改,以避免与客户端默认值不一致发生冲突。 以下 Api 已将默认值从 SameSiteMode.Lax
更改为 -1
,以避免发出这些 cookie 的 SameSite 属性:
- 与HttpContext一起使用的 CookieOptions
- CookieBuilder 用作
CookieOptions
的工厂 - CookiePolicyOptions.MinimumSameSitePolicy
历史记录和更改
SameSite 支持在2.0 中第一次 ASP.NET Core 实现,使用2016 草案标准。 2016标准已选择加入。 ASP.NET Core 选择在默认情况下 Lax
的几个 cookie。 在遇到身份验证的几个问题后,大多数 SameSite 使用已禁用。
2019年11月发布了修补程序,从2016标准版更新为2019标准。 SameSite 规范的2019草案:
- 不向后兼容2016草案。 有关详细信息,请参阅本文档中的支持旧版浏览器。
- 指定默认情况下将 cookie 视为
SameSite=Lax
。 - 指定显式断言
SameSite=None
以便启用跨站点传递的 cookie 应标记为Secure
。None
是选择退出的新项。 - 为 ASP.NET Core 2.1、2.2 和3.0 颁发的修补程序支持。 ASP.NET Core 3.1 具有附加的 SameSite 支持。
- 默认情况下,计划在2020 年2月启用。 浏览器已开始在2019中移动到此标准。
受从 2016 SameSite 草案标准到2019草案标准的更改影响的 Api
- SameSiteMode
- CookieOptions. SameSite
- CookieBuilder. SameSite
- CookiePolicyOptions.MinimumSameSitePolicy
- Microsoft.Net.Http.Headers.SameSiteMode
- Microsoft.Net.Http.Headers.SetCookieHeaderValue.SameSite
支持旧版浏览器
2016 SameSite 标准规定,未知值必须被视为 SameSite=Strict
值。 从支持 2016 SameSite 标准的旧版浏览器访问的应用可能会在收到值为 "None
" 的 SameSite 属性时中断。 如果 Web 应用要支持较旧的浏览器,则必须实现浏览器检测。 ASP.NET Core 不会实现浏览器检测,因为用户代理值非常稳定且频繁更改。 Microsoft.AspNetCore.CookiePolicy 中的扩展点允许插入特定于用户代理的逻辑。
在 Startup.Configure
中,添加调用 UseCookiePolicy 的代码,然后调用 UseAuthentication 或任何写入 cookie 的方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseCookiePolicy(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); }
在 Startup.ConfigureServices
中,添加类似于下面的代码:
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); }); services.AddRazorPages(); } private void CheckSameSite(HttpContext httpContext, CookieOptions options) { if (options.SameSite == SameSiteMode.None) { var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent)) { options.SameSite = SameSiteMode.Unspecified; } } }
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = (SameSiteMode)(-1); options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); }); services.AddRazorPages(); } private void CheckSameSite(HttpContext httpContext, CookieOptions options) { if (options.SameSite == SameSiteMode.None) { var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent)) { options.SameSite = (SameSiteMode)(-1); } } }
在前面的示例中,MyUserAgentDetectionLib.DisallowsSameSiteNone
是一个用户提供的库,用于检测用户代理是否不支持 SameSite None
:
if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent)) { options.SameSite = SameSiteMode.Unspecified; }
下面的代码演示 DisallowsSameSiteNone
方法示例:
警告
以下代码仅用于演示:
- 不应将其视为已完成。
- 它不维护或不受支持。
public static bool DisallowsSameSiteNone(string userAgent) { // Check if a null or empty string has been passed in, since this // will cause further interrogation of the useragent to fail. if (String.IsNullOrWhiteSpace(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 问题
与远程站点(如通过第三方登录)交互的应用需要:
- 测试多个浏览器上的交互。
- 应用本文档中讨论的CookiePolicy 浏览器检测和缓解措施。
使用可选择新的 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、ADAL 或所使用的任何库,测试 Safari 12、Safari 13 和基于 WebKit 的 OS 样式登录。 问题取决于基础 OS 版本。 已知 OSX Mojave (10.14)和 iOS 12 与新的 SameSite 行为存在兼容性问题。 将 OS 升级到 OSX Catalina (10.15)或 iOS 13 会解决此问题。 Safari 当前没有用于测试新规范行为的选择标记。
用 Firefox 测试
可以在版本 68 + 上测试对新标准的 Firefox 支持,方法是在 "about:config
" 页面上选择 "network.cookie.sameSite.laxByDefault
的功能标志。 以前版本的 Firefox 没有出现兼容性问题的报告。
通过 Edge 浏览器进行测试
Edge 支持旧的 SameSite 标准。 边缘版本44与新的标准没有任何已知的兼容性问题。
带边缘测试(Chromium)
在 edge://flags/#same-site-by-default-cookies
页上设置 SameSite 标志。 未发现边缘 Chromium 的兼容性问题。
用 Electron 进行测试
Electron 的版本包括较早版本的 Chromium。 例如,团队使用的 Electron 版本为 Chromium 66,该版本展示了较旧的行为。 你必须使用你的产品使用的 Electron 版本执行你自己的兼容性测试。 请参阅下一节中的支持旧版浏览器。