SameSite 是一种 IETF 草案标准,旨在针对跨网站请求伪造 (CSRF) 攻击提供一些保护。 最初于 2016 年起草,草案标准于 2019 年更新。 更新后的标准与以前的标准不向后兼容,以下是最明显的差异:
- 默认情况下,没有 SameSite 标头的 Cookie 被视为 SameSite=Lax。
- 
              SameSite=None必须用于允许进行跨站点 cookie 使用。
- 断言 SameSite=None的 Cookie 也必须标记为Secure。
- 使用 <iframe>的应用程序可能会遇到与sameSite=Lax或sameSite=StrictCookie 有关的问题,因为<iframe>被视为跨站点方案。
- 
              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) 会执行这些操作。
SameSite 测试示例代码
可下载和测试以下示例:
| Sample | Document | 
|---|---|
| Razor 页面 | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 | 
对 sameSite 属性的 .NET 支持
.NET 支持 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 中,添加在调用 UseCookiePolicy 或写入 Cookie 的UseAuthentication方法之前调用  的代码:
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 方法:
Warning
下面的代码仅用于演示:
- 不应将其视为完整代码。
- 它不进行维护或受支持。
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 严格执行了先前的草案,在 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 测试
通过在具有功能标志 about:config 的 network.cookie.sameSite.laxByDefault 页面上选择加入,可在版本 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
Additional resources
| Sample | Document | 
|---|---|
| Razor 页面 | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 | 
可下载和测试以下示例:
| Sample | Document | 
|---|---|
| Razor 页面 | 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.Unspecified 并将额外的枚举值添加到 SameSiteMode 枚举中。
此新值指示在使用 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 中,添加在调用 UseCookiePolicy 或写入 Cookie 的UseAuthentication方法之前调用  的代码:
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 方法:
Warning
下面的代码仅用于演示:
- 不应将其视为完整代码。
- 它不进行维护或受支持。
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 严格执行了先前的草案,在 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 测试
通过在具有功能标志 about:config 的 network.cookie.sameSite.laxByDefault 页面上选择加入,可在版本 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
Additional resources
| Sample | Document | 
|---|---|
| Razor 页面 | ASP.NET Core 3.1 Razor Pages SameSite cookie 示例 | 
可下载和测试以下示例:
| Sample | Document | 
|---|---|
| MVC | ASP.NET Core 2.1 MVC SameSite cookie 示例 | 
| Razor 页面 | ASP.NET Core 2.1 Razor Pages SameSite cookie 示例 | 
12 月补丁行为变更
针对 .NET Framework 和 .NET Core 2.1 的特定行为变更是 SameSite 属性如何解释 None 值。 在发布补丁之前,值 None 表示“完全不发出属性”,在发布补丁之后,它表示“发出值为 None 的属性”。 发布补丁之后,SameSite 的 (SameSiteMode)(-1) 值会导致不发出属性。
窗体身份验证和会话状态 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 中,添加在调用 UseCookiePolicy 或写入 Cookie 的UseAuthentication方法之前调用  的代码:
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 方法:
Warning
下面的代码仅用于演示:
- 不应将其视为完整代码。
- 它不进行维护或受支持。
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 严格执行了先前的草案,在 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 测试
通过在具有功能标志 about:config 的 network.cookie.sameSite.laxByDefault 页面上选择加入,可在版本 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 版本执行你自己的兼容性测试。 请参阅以下部分中的支持较旧浏览器。
Additional resources
| Sample | Document | 
|---|---|
| MVC | ASP.NET Core 2.1 MVC SameSite cookie 示例 | 
| Razor 页面 | ASP.NET Core 2.1 Razor Pages SameSite cookie 示例 |