cookie在 ASP.NET Core 中使用 SameSiteWork with SameSite cookies in ASP.NET Core

作者:Rick AndersonBy Rick Anderson

SameSite 是一項 IETF 草稿標準,其設計目的是為了提供某些保護,以防止跨網站偽造要求 (CSRF) 攻擊。SameSite is an IETF draft standard designed to provide some protection against cross-site request forgery (CSRF) attacks. 最初是在 2016中繪製草稿標準,已于 2019更新。Originally drafted in 2016, the draft standard was updated in 2019. 更新的標準與先前的標準沒有回溯相容性,但以下是最顯著的差異:The updated standard is not backward compatible with the previous standard, with the following being the most noticeable differences:

  • Cookie預設會將不含 SameSite 標頭的 s 視為 SameSite=LaxCookies without SameSite header are treated as SameSite=Lax by default.
  • SameSite=None 必須用來允許跨網站 cookie 使用。SameSite=None must be used to allow cross-site cookie use.
  • Cookie判斷提示的 SameSite=None 也必須標示為 SecureCookies that assert SameSite=None must also be marked as Secure.
  • 使用的應用程式 <iframe> 可能會遇到或的問題, sameSite=Lax sameSite=Strict cookie 因為 <iframe> 會被視為跨網站案例。Applications that use <iframe> may experience issues with sameSite=Lax or sameSite=Strict cookies because <iframe> is treated as cross-site scenarios.
  • SameSite=None 2016 標準不允許此值,而且會導致某些執行將這類視為 cookie SameSite=StrictThe value SameSite=None is not allowed by the 2016 standard and causes some implementations to treat such cookies as SameSite=Strict. 請參閱本檔中的 支援舊版瀏覽器See Supporting older browsers in this document.

SameSite=Lax 設定適用于大部分的應用程式 cookie 。The SameSite=Lax setting works for most application cookies. 某些形式的驗證,例如 OpenID Connect (OIDC) 和 WS-同盟 預設為以 POST 為基礎的重新導向。Some forms of authentication like OpenID Connect (OIDC) and WS-Federation default to POST based redirects. 以 POST 為基礎的重新導向會觸發 SameSite 瀏覽器保護,因此這些元件會停用 SameSite。The POST based redirects trigger the SameSite browser protections, so SameSite is disabled for these components. 由於要求流程的差異,大部分的 OAuth 登入不會受到影響。Most OAuth logins are not affected due to differences in how the request flows.

每個發出的 ASP.NET Core 元件都 cookie 需要決定 SameSite 是否適當。Each ASP.NET Core component that emits cookies needs to decide if SameSite is appropriate.

SameSite 和 IdentitySameSite and Identity

ASP.NET Core 身分識別主要不會受到SameSite cookie的影響,除了像是或整合等 advanced 案例 IFrames OpenIdConnectASP.NET Core Identity is largely unaffected by SameSite cookies except for advanced scenarios like IFrames or OpenIdConnect integration.

使用時 Identity ,請不要新增任何 cookie 提供者或呼叫 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) ,而 Identity 是會負責處理。When using Identity, do not add any cookie providers or call services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme), Identity takes care of that.

SameSite 測試範例程式碼SameSite test sample code

您可以下載並測試下列範例:The following samples can be downloaded and tested:

範例Sample 文件Document
.NET Core MVC.NET Core MVC ASP.NET Core 2.1 MVC SameSite cookie 範例
.NET Core Razor 頁面.NET Core Razor Pages ASP.NET Core 2.1 Razor 頁面 SameSite cookie 範例

您可以下載並測試下列範例:The following sample can be downloaded and tested:

範例Sample 文件Document
.NET Core Razor 頁面.NET Core Razor Pages ASP.NET Core 3.1 Razor 頁面 SameSite cookie 範例

SameSite 屬性的 .NET Core 支援.NET Core support for the sameSite attribute

.NET Core 2.2 和更新版本支援 SameSite 自12月2019版更新後的 2019 draft 標準。.NET Core 2.2 and later support the 2019 draft standard for SameSite since the release of updates in December 2019. 開發人員可以使用屬性,以程式設計方式控制 sameSite 屬性的值 HttpCookie.SameSiteDevelopers are able to programmatically control the value of the sameSite attribute using the HttpCookie.SameSite property. 將屬性設為 [嚴格]、[不嚴格] 或 [無],會 SameSite 導致這些值使用來寫入至網路 cookie 。Setting the SameSite property to Strict, Lax, or None results in those values being written on the network with the cookie. 將它設定為等於 (SameSiteMode)(-1) ,表示網路上沒有任何 sameSite 屬性應該包含在 cookieSetting it equal to (SameSiteMode)(-1) indicates that no sameSite attribute should be included on the network with the cookie

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.0 和更新版本支援更新的 SameSite 值,並將額外的列舉值新增 SameSiteMode.UnspecifiedSameSiteMode 列舉。.NET Core 3.0 and later support the updated SameSite values and adds an extra enum value, SameSiteMode.Unspecified to the SameSiteMode enum. 這個新值表示不應該傳送任何 sameSite cookie 。This new value indicates no sameSite should be sent with the cookie.

12月的修補程式列為變更December patch behavior changes

.NET Framework 和 .NET Core 2.1 的特定行為變更是 SameSite 屬性如何解讀值的方式 NoneThe specific behavior change for .NET Framework and .NET Core 2.1 is how the SameSite property interprets the None value. 在修補程式的值「完全 None 不發出屬性」之前,在修補程式之後,它表示「發出具有值的屬性」 NoneBefore the patch a value of None meant "Do not emit the attribute at all", after the patch it means "Emit the attribute with a value of None". 在 patch 的值之後,將 SameSite (SameSiteMode)(-1) 不會發出屬性。After the patch a SameSite value of (SameSiteMode)(-1) causes the attribute not to be emitted.

表單驗證和會話狀態 s 的預設 SameSite 值 cookie 已從變更 NoneLaxThe default SameSite value for forms authentication and session state cookies was changed from None to Lax.

使用 SameSite 的 API 使用方式API usage with SameSite

HttpCoNtext. 回應。 Cookie。 Append 預設值為 Unspecified ,表示未將 SameSite 屬性新增至, cookie 而且用戶端會使用其預設行為 (不嚴格的新瀏覽器,) 不會有舊的瀏覽器。HttpContext.Response.Cookies.Append defaults to Unspecified, meaning no SameSite attribute added to the cookie and the client will use its default behavior (Lax for new browsers, None for old ones). 下列程式碼顯示如何將 cookie SameSite 值變更為 SameSiteMode.LaxThe following code shows how to change the cookie SameSite value to SameSiteMode.Lax:

HttpContext.Response.Cookies.Append(
                     "name", "value",
                     new CookieOptions() { SameSite = SameSiteMode.Lax });

發出的所有 ASP.NET Core 元件都會 cookie 以適用于其案例的設定覆寫先前的預設值。All ASP.NET Core components that emit cookies override the preceding defaults with settings appropriate for their scenarios. 覆寫的先前預設值尚未變更。The overridden preceding default values haven't changed.

元件Component cookie 預設Default
CookieBuilder SameSite Unspecified
Session SessionOptions.CookieSessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.CookieCookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.CookieAntiforgeryOptions.Cookie Strict
Cookie 認證Cookie Authentication CookieAuthenticationOptions.CookieCookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions。州 CookieTwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions 相互關聯Cookie NoneRemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptionsCookieOpenIdConnectOptions.NonceCookie None
HttpCoNtext. 回應。 Cookies. 附加HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 和更新版本提供下列 SameSite 支援:ASP.NET Core 3.1 and later provides the following SameSite support:

  • 重新定義 SameSiteMode.None 要發出的行為 SameSite=NoneRedefines the behavior of SameSiteMode.None to emit SameSite=None
  • 新增值 SameSiteMode.Unspecified 以省略 SameSite 屬性。Adds a new value SameSiteMode.Unspecified to omit the SameSite attribute.
  • 所有的 cookie api 都會預設為 UnspecifiedAll cookies APIs default to Unspecified. 某些使用 cookie 來設定值的元件會更明確地使用其案例。Some components that use cookies set values more specific to their scenarios. 如需範例,請參閱上表。See the table above for examples.

在 ASP.NET Core 3.0 和更新版本中,SameSite 預設值已變更,以避免與不一致的用戶端預設值發生衝突。In ASP.NET Core 3.0 and later the SameSite defaults were changed to avoid conflicting with inconsistent client defaults. 下列 Api 已將預設值從變更 SameSiteMode.Lax -1 ,以避免發出下列的 SameSite 屬性 cookie :The following APIs have changed the default from SameSiteMode.Lax to -1 to avoid emitting a SameSite attribute for these cookies:

歷程記錄和變更History and changes

SameSite 支援首次使用 2016 draft 標準在2.0 的 ASP.NET Core 中執行。SameSite support was first implemented in ASP.NET Core in 2.0 using the 2016 draft standard. 2016標準為加入宣告。The 2016 standard was opt-in. 依預設,將數個設定 cookie 為,ASP.NET Core 加入宣告 LaxASP.NET Core opted-in by setting several cookies to Lax by default. 在遇到幾個驗證 問題 之後,就會 停用大部分的 SameSite 使用量。After encountering several issues with authentication, most SameSite usage was disabled.

修補程式 于2019年11月發行,以從2016標準更新為2019標準。Patches were issued in November 2019 to update from the 2016 standard to the 2019 standard. SameSite 規格的2019草稿The 2019 draft of the SameSite specification:

  • 與 2016 draft 相容。Is not backwards compatible with the 2016 draft. 如需詳細資訊,請參閱本檔中的 支援舊版瀏覽器For more information, see Supporting older browsers in this document.
  • cookie預設會將指定視為 SameSite=LaxSpecifies cookies are treated as SameSite=Lax by default.
  • 指定明確判斷提示的 cookie ,以便 SameSite=None 啟用跨網站傳遞應標示為 SecureSpecifies cookies that explicitly assert SameSite=None in order to enable cross-site delivery should be marked as Secure. None 是要退出的新專案。None is a new entry to opt out.
  • 針對 ASP.NET Core 2.1、2.2 和3.0 發出的修補程式支援。Is supported by patches issued for ASP.NET Core 2.1, 2.2, and 3.0. ASP.NET Core 3.1 有額外的 SameSite 支援。ASP.NET Core 3.1 has additional SameSite support.
  • 預設會在2020 年2月Chrome啟用。Is scheduled to be enabled by Chrome by default in Feb 2020. 瀏覽器已在2019中開始移至此標準。Browsers started moving to this standard in 2019.

受 2016 SameSite 草稿標準變更為 2019 draft 標準的 ApiAPIs impacted by the change from the 2016 SameSite draft standard to the 2019 draft standard

支援較舊的瀏覽器Supporting older browsers

2016 SameSite 標準規定必須將未知值視為 SameSite=Strict 值。The 2016 SameSite standard mandated that unknown values must be treated as SameSite=Strict values. 從較舊的瀏覽器存取的應用程式(支援 2016 SameSite 標準)在取得具有值的 SameSite 屬性時可能會中斷 NoneApps accessed from older browsers which support the 2016 SameSite standard may break when they get a SameSite property with a value of None. 如果 Web 應用程式想要支援較舊的瀏覽器,則必須執行瀏覽器偵測。Web apps must implement browser detection if they intend to support older browsers. ASP.NET Core 不會執行瀏覽器偵測,因為 User-Agents 值會高度變動且經常變更。ASP.NET Core doesn't implement browser detection because User-Agents values are highly volatile and change frequently. 中的擴充點 Microsoft.AspNetCore.CookiePolicy 可讓您插入 User-Agent 的特定邏輯。An extension point in Microsoft.AspNetCore.CookiePolicy allows plugging in User-Agent specific logic.

在中 Startup.Configure ,加入呼叫之前呼叫的程式碼, UseCookiePolicy UseAuthentication任何 寫入 s 的方法 cookie :In Startup.Configure, add code that calls UseCookiePolicy before calling UseAuthentication or any method that writes cookies:

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 ,加入類似下列的程式碼:In Startup.ConfigureServices, add code similar to the following:

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 NoneIn the preceding sample, MyUserAgentDetectionLib.DisallowsSameSiteNone is a user supplied library that detects if the user agent doesn't support SameSite None:

if (MyUserAgentDetectionLib.DisallowsSameSiteNone(userAgent))
{
    options.SameSite = SameSiteMode.Unspecified;
}

下列程式碼顯示範例 DisallowsSameSiteNone 方法:The following code shows a sample DisallowsSameSiteNone method:

警告

下列程式碼僅供示範之用:The following code is for demonstration only:

  • 不應將其視為已完成。It should not be considered complete.
  • 不會維護或支援。It is not maintained or supported.
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 問題Test apps for SameSite problems

與遠端網站互動的應用程式(例如透過協力廠商登入)必須:Apps that interact with remote sites such as through third-party login need to:

使用可加入新 SameSite 行為的用戶端版本,測試 web 應用程式。Test web apps using a client version that can opt-in to the new SameSite behavior. Chrome、Firefox 和 Chromium Edge 都有新的加入宣告功能旗標,可用於測試。Chrome, Firefox, and Chromium Edge all have new opt-in feature flags that can be used for testing. 當您的應用程式套用 SameSite 修補程式之後,請使用較舊的用戶端版本(特別是 Safari)進行測試。After your app applies the SameSite patches, test it with older client versions, especially Safari. 如需詳細資訊,請參閱本檔中的 支援舊版瀏覽器For more information, see Supporting older browsers in this document.

使用 Chrome 測試Test with Chrome

Chrome 78 + 提供誤導的結果,因為它有暫時的緩和措施。Chrome 78+ gives misleading results because it has a temporary mitigation in place. Chrome 78 + 暫時緩和可允許 cookie 不到兩分鐘的舊。The Chrome 78+ temporary mitigation allows cookies less than two minutes old. 使用已啟用適當測試旗標的 Chrome 76 或77可提供更精確的結果。Chrome 76 or 77 with the appropriate test flags enabled provides more accurate results. 測試新的 SameSite 行為切換 chrome://flags/#same-site-by-default-cookies啟用狀態To test the new SameSite behavior toggle chrome://flags/#same-site-by-default-cookies to Enabled. 較舊版本的 Chrome (75 和以下的) 會回報為失敗,並出現新的 None 設定。Older versions of Chrome (75 and below) are reported to fail with the new None setting. 請參閱本檔中的 支援舊版瀏覽器See Supporting older browsers in this document.

Google 未提供較舊的 chrome 版本。Google does not make older chrome versions available. 遵循 下載 Chromium 中的指示,測試舊版的 Chrome。Follow the instructions at Download Chromium to test older versions of Chrome. 請勿從搜尋較舊版本 chrome 所提供的 連結下載 ChromeDo not download Chrome from links provided by searching for older versions of chrome.

從未放大的版本開始 80.0.3975.0 ,您可以使用新的旗標來停用寬鬆的 + POST 暫時緩和措施, --enable-features=SameSiteDefaultChecksMethodRigorously 以在移除緩和措施的功能的最終狀態下測試網站和服務。Starting in Canary version 80.0.3975.0, the Lax+POST temporary mitigation can be disabled for testing purposes using the new flag --enable-features=SameSiteDefaultChecksMethodRigorously to allow testing of sites and services in the eventual end state of the feature where the mitigation has been removed. 如需詳細資訊,請參閱 Chromium Projects SameSite 更新For more information, see The Chromium Projects SameSite Updates

使用 Safari 測試Test with Safari

Safari 12 會嚴格地實作為先前的草稿,並 None 在新值為時失敗 cookie 。Safari 12 strictly implemented the prior draft and fails when the new None value is in a cookie. None 可透過支援本檔中 舊版流覽 器的瀏覽器偵測程式碼來避免。None is avoided via the browser detection code Supporting older browsers in this document. 使用 MSAL、ADAL 或您所使用的任何程式庫,測試 Safari 12、Safari 13 和以 WebKit 為基礎的 OS 樣式登入。Test Safari 12, Safari 13, and WebKit based OS style logins using MSAL, ADAL or whatever library you are using. 問題取決於基礎作業系統版本。The problem is dependent on the underlying OS version. 已知 OSX Mojave (10.14) 和 iOS 12 具有新 SameSite 行為的相容性問題。OSX Mojave (10.14) and iOS 12 are known to have compatibility problems with the new SameSite behavior. 將作業系統升級至 OSX Catalina (10.15) 或 iOS 13 可修正問題。Upgrading the OS to OSX Catalina (10.15) or iOS 13 fixes the problem. Safari 目前沒有可用於測試新規格行為的加入宣告旗標。Safari does not currently have an opt-in flag for testing the new spec behavior.

使用 Firefox 進行測試Test with Firefox

您可以在頁面上選擇具有功能旗標的,以在版本 68 + 上測試新標準的 Firefox 支援 about:config network.cookie.sameSite.laxByDefaultFirefox support for the new standard can be tested on version 68+ by opting in on the about:config page with the feature flag network.cookie.sameSite.laxByDefault. 舊版 Firefox 沒有相容性問題的報告。There haven't been reports of compatibility issues with older versions of Firefox.

使用 Edge 瀏覽器進行測試Test with Edge browser

Edge 支援舊的 SameSite 標準。Edge supports the old SameSite standard. Edge 版本44沒有任何已知的新標準相容性問題。Edge version 44 doesn't have any known compatibility problems with the new standard.

使用 Edge (Chromium) 進行測試Test with Edge (Chromium)

SameSite 旗標是在頁面上設定的 edge://flags/#same-site-by-default-cookiesSameSite flags are set on the edge://flags/#same-site-by-default-cookies page. Edge Chromium 未發現任何相容性問題。No compatibility issues were discovered with Edge Chromium.

測試方式 ElectronTest with Electron

的版本 Electron 包括較舊版本的 Chromium。Versions of Electron include older versions of Chromium. 例如,小組所使用的版本 Electron 是 Chromium 66,這會展示較舊的行為。For example, the version of Electron used by Teams is Chromium 66, which exhibits the older behavior. 您必須使用您的產品版本來執行您自己的相容性測試 Electron 。You must perform your own compatibility testing with the version of Electron your product uses. 請參閱下一節中的 支援舊版瀏覽器See Supporting older browsers in the following section.

其他資源Additional resources