在 ASP.NET Core 中使用 SameSite Cookie
SameSite 是一种 IETF 草案标准,旨在针对跨网站请求伪造 (CSRF) 攻击提供一些保护。 最初于 2016 年起草,草案标准于 2019 年更新。 更新后的标准与以前的标准不向后兼容,以下是最明显的差异:
- 默认情况下,没有 SameSite 标头的 Cookie 被视为
SameSite=Lax
。 SameSite=None
必须用于允许进行跨站点 cookie 使用。- 断言
SameSite=None
的 Cookie 也必须标记为Secure
。 - 使用
<iframe>
的应用程序可能会遇到与sameSite=Lax
或sameSite=Strict
Cookie 有关的问题,因为<iframe>
被视为跨站点方案。 - 2016 标准不允许使用值
SameSite=None
,它会导致某些实现将此类 Cookie 视为SameSite=Strict
。 请参阅本文档中的支持较旧浏览器。
SameSite=Lax
设置适用于大多数应用程序 Cookie。 某些形式的身份验证(如 OpenID Connect (OIDC) 和 WS-Federation)默认为基于 POST 的重定向。 基于 POST 的重定向会触发 SameSite 浏览器保护,因此会为这些组件禁用 SameSite。 由于请求流程不同,大多数 OAuth 登录不受影响。
发出 Cookie 的每个 ASP.NET Core 组件都需要确定 SameSite 是否合适。
SameSite 和 Identity
ASP.NET Core Identity 在很大程度上不受 SameSite Cookie 的影响,但 IFrames
或 OpenIdConnect
集成等高级方案除外。
使用 Identity
时,不要添加任何 cookie 提供程序或调用 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
,Identity
会执行这些操作。
SameSite 测试示例代码
可下载和测试以下示例:
示例 | 文档 |
---|---|
.NET Core Razor Pages | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 |
.NET Core 对 sameSite 属性的支持
.NET Core 支持 SameSite 的 2019 年草案标准。 开发人员能够使用 HttpCookie.SameSite
属性以编程方式控制 sameSite 特性的值。 将 SameSite
属性设置为 Strict
、Lax
或 None
会导致在使用 cookie 的情况下在网络上写入这些值。 设置为 SameSiteMode.Unspecified
指示在使用 cookie 的情况下不应发送 sameSite。
var cookieOptions = new CookieOptions
{
// Set the secure flag, which Chrome's changes will require for SameSite none.
// Note this will also require you to be running on HTTPS.
Secure = true,
// Set the cookie to HTTP only which is good practice unless you really do need
// to access it client side in scripts.
HttpOnly = true,
// Add the SameSite attribute, this will emit the attribute with a value of none.
SameSite = SameSiteMode.None
// The client should follow its default cookie policy.
// SameSite = SameSiteMode.Unspecified
};
// Add the cookie to the response cookie collection
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
}
使用 SameSite 的 API 用法
HttpContext.Response.Cookies.Append 默认为 Unspecified
,这意味着不会向 cookie 添加任何 SameSite 属性,并且客户端会使用其默认行为(新浏览器为 Lax,旧浏览器为 None)。 下面的代码演示如何将 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 属性:
- CookieOptions 与 HttpContext.Response.Cookies.Append 一起使用
- CookieBuilder 用作
CookieOptions
的工厂 - CookiePolicyOptions.MinimumSameSitePolicy
历史记录和更改
SameSite 支持首先在 ASP.NET Core 2.0 中使用 2016 年草案标准来实现。 2016 标准是选择加入的。 默认情况下,ASP.NET Core 通过将多个 Cookie 设置为 Lax
来选择加入。 在遇到身份验证方面的几个问题后,大多数 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 月由 Chrome 默认启用。 浏览器已开始在 2019 年迁移到此标准。
受从 2016 SameSite 草案标准到 2019 草案标准的更改影响的 API
- Http.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 未实现浏览器检测,因为 User-Agent 值非常不稳定,经常更改。 Microsoft.AspNetCore.CookiePolicy 中的扩展点允许插入特定于用户代理的逻辑。
在 Program.cs
中,添加在调用 UseAuthentication 或写入 Cookie 的任何方法之前调用 UseCookiePolicy 的代码:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
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;
}
}
}
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
在 Program.cs
中,添加与以下突出显示的代码类似的代码:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
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;
}
}
}
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
在上面的示例中,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。
从 Canary 版本 80.0.3975.0
开始,可以使用新标志 --enable-features=SameSiteDefaultChecksMethodRigorously
为进行测试而禁用 Lax+POST 临时缓解,以便允许在删除了缓解的功能的最终状态下测试站点和服务。 有关详细信息,请参阅 Chromium 项目 SameSite 更新
使用 Safari 测试
Safari 12 严格执行了先前的草案,在 cookie 中存在新 None
值时会失败。 可通过本文档中浏览器检测代码支持较旧浏览器避免 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
其他资源
示例 | 文档 |
---|---|
.NET Core Razor Pages | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 |
可下载和测试以下示例:
示例 | 文档 |
---|---|
.NET Core Razor Pages | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 |
.NET Core 对 sameSite 属性的支持
.NET Core 3.1 及更高版本支持 SameSite 的 2019 年草案标准。 开发人员能够使用 HttpCookie.SameSite
属性以编程方式控制 sameSite 特性的值。 将 SameSite
属性设置为 Strict、Lax 或 None 会导致在使用 cookie 的情况下在网络上写入这些值。 将其设置为等于 (SameSiteMode)(-1)
指示在使用 cookie 的情况下,网络中不应包含 sameSite 属性
var cookieOptions = new CookieOptions
{
// Set the secure flag, which Chrome's changes will require for SameSite none.
// Note this will also require you to be running on HTTPS.
Secure = true,
// Set the cookie to HTTP only which is good practice unless you really do need
// to access it client side in scripts.
HttpOnly = true,
// Add the SameSite attribute, this will emit the attribute with a value of none.
// To not emit the attribute at all set
// SameSite = (SameSiteMode)(-1)
SameSite = SameSiteMode.None
};
// Add the cookie to the response cookie collection
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
.NET Core 3.1 及更高版本支持更新后的 SameSite 值,并向 SameSiteMode
枚举添加额外枚举值 SameSiteMode.Unspecified
。
此新值指示在使用 cookie 的情况下不应发送 sameSite。
使用 SameSite 的 API 用法
HttpContext.Response.Cookies.Append 默认为 Unspecified
,这意味着不会向 cookie 添加任何 SameSite 属性,并且客户端会使用其默认行为(新浏览器为 Lax,旧浏览器为 None)。 下面的代码演示如何将 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 属性:
- CookieOptions 与 HttpContext.Response.Cookies.Append 一起使用
- CookieBuilder 用作
CookieOptions
的工厂 - CookiePolicyOptions.MinimumSameSitePolicy
历史记录和更改
SameSite 支持首先在 ASP.NET Core 2.0 中使用 2016 年草案标准来实现。 2016 标准是选择加入的。 默认情况下,ASP.NET Core 通过将多个 Cookie 设置为 Lax
来选择加入。 在遇到身份验证方面的几个问题后,大多数 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 月由 Chrome 默认启用。 浏览器已开始在 2019 年迁移到此标准。
受从 2016 SameSite 草案标准到 2019 草案标准的更改影响的 API
- Http.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 未实现浏览器检测,因为 User-Agent 值非常不稳定,经常更改。 Microsoft.AspNetCore.CookiePolicy 中的扩展点允许插入特定于用户代理的逻辑。
在 Startup.Configure
中,添加在调用 UseAuthentication 或写入 Cookie 的任何方法之前调用 UseCookiePolicy 的代码:
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;
}
}
}
在上面的示例中,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。
从 Canary 版本 80.0.3975.0
开始,可以使用新标志 --enable-features=SameSiteDefaultChecksMethodRigorously
为进行测试而禁用 Lax+POST 临时缓解,以便允许在删除了缓解的功能的最终状态下测试站点和服务。 有关详细信息,请参阅 Chromium 项目 SameSite 更新
使用 Safari 测试
Safari 12 严格执行了先前的草案,在 cookie 中存在新 None
值时会失败。 可通过本文档中浏览器检测代码支持较旧浏览器避免 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
其他资源
示例 | 文档 |
---|---|
.NET Core Razor Pages | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 |
可下载和测试以下示例:
示例 | 文档 |
---|---|
.NET Core MVC | ASP.NET Core 2.1 MVC SameSite cookie 示例 |
.NET Core Razor Pages | ASP.NET Core 2.1 Razor Pages SameSite cookie 示例 |
12 月补丁行为变更
针对 .NET Framework 和 .NET Core 2.1 的特定行为变更是 SameSite
属性如何解释 None
值。 在发布补丁之前,值 None
表示“完全不发出属性”,在发布补丁之后,它表示“发出值为 None
的属性”。 发布补丁之后,(SameSiteMode)(-1)
的 SameSite
值会导致不发出属性。
窗体身份验证和会话状态 Cookie 的默认 SameSite 值已从 None
更改为 Lax
。
使用 SameSite 的 API 用法
HttpContext.Response.Cookies.Append 默认为 Unspecified
,这意味着不会向 cookie 添加任何 SameSite 属性,并且客户端会使用其默认行为(新浏览器为 Lax,旧浏览器为 None)。 下面的代码演示如何将 cookie SameSite 值更改为 SameSiteMode.Lax
:
HttpContext.Response.Cookies.Append(
"name", "value",
new CookieOptions() { SameSite = SameSiteMode.Lax });
发出 Cookie 的所有 ASP.NET Core 组件都会使用适合其场景的设置替代前面的默认值。 前面替代的默认值未更改。
历史记录和更改
SameSite 支持首先在 ASP.NET Core 2.0 中使用 2016 年草案标准来实现。 2016 标准是选择加入的。 默认情况下,ASP.NET Core 通过将多个 Cookie 设置为 Lax
来选择加入。 在遇到身份验证方面的几个问题后,大多数 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 月由 Chrome 默认启用。 浏览器已开始在 2019 年迁移到此标准。
受从 2016 SameSite 草案标准到 2019 草案标准的更改影响的 API
- Http.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 未实现浏览器检测,因为 User-Agent 值非常不稳定,经常更改。 Microsoft.AspNetCore.CookiePolicy 中的扩展点允许插入特定于用户代理的逻辑。
在 Startup.Configure
中,添加在调用 UseAuthentication 或写入 Cookie 的任何方法之前调用 UseCookiePolicy 的代码:
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)(-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。
从 Canary 版本 80.0.3975.0
开始,可以使用新标志 --enable-features=SameSiteDefaultChecksMethodRigorously
为进行测试而禁用 Lax+POST 临时缓解,以便允许在删除了缓解的功能的最终状态下测试站点和服务。 有关详细信息,请参阅 Chromium 项目 SameSite 更新
使用 Safari 测试
Safari 12 严格执行了先前的草案,在 cookie 中存在新 None
值时会失败。 可通过本文档中浏览器检测代码支持较旧浏览器避免 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
其他资源
示例 | 文档 |
---|---|
.NET Core MVC | ASP.NET Core 2.1 MVC SameSite cookie 示例 |
.NET Core Razor Pages | ASP.NET Core 2.1 Razor Pages SameSite cookie 示例 |