在 ASP.NET Core 中使用 SameSite cookie

作者:Rick Anderson

SameSite 是 IETF 草稿標準,旨在提供對跨網站偽造要求 (CSRF) 攻擊的一些防禦。 最初於 2016 年制定草稿,草稿標準於 2019 年更新。 更新的標準不回溯相容於先前的標準,以下是最明顯的差異:

  • 依預設會將沒有 SameSite 標頭的 Cookie 視為 SameSite=Lax
  • 必須使用 SameSite=None 以允許跨網站 cookie 使用。
  • 判斷提示 SameSite=None 的 Cookie 也必須標示為 Secure
  • 使用 <iframe> 的應用程式可能會遇到 sameSite=LaxsameSite=Strictcookie 的問題,因為 <iframe> 會被視為跨網站案例。
  • 2016 標準不允許值 SameSite=None,而導致某些實作將這類 cookie 視為 SameSite=Strict。 請參閱本文件中的支援舊版瀏覽器

SameSite=Lax 設定適用於大部分的應用程式 cookie。 某些形式的驗證 (例如 OpenID Connect (OIDC) 和 WS 同盟) 預設為 POST 型重新導向。 POST 型重新導向會觸發 SameSite 瀏覽器保護,因此對於這些元件會停用 SameSite。 大部分的 OAuth 登入並不會因為要求傳輸方式的差異而受到影響。

每個發出 cookie 的 ASP.NET Core 元件都必須確認 SameSite 是否適當。

SameSite 和 Identity

除了 IFramesOpenIdConnect 整合等進階案例以外,ASP.NET Core Identity 基本上不受 SameSite cookie 影響。

使用 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 屬性設定為 StrictLaxNone,會導致這些值透過 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);
}

將 API 與 SameSite 搭配使用

HttpContext.Response.Cookies.Append 預設為 Unspecified,這表示沒有任何 SameSite 屬性新增至 cookie,且用戶端會使用其預設行為 (新瀏覽器為 Lax,舊瀏覽器為 None)。 下列程式碼說明如何將 cookie SameSite 值變更為 SameSiteMode.Lax

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

所有發出 cookie 的 ASP.NET Core 元件都會將前述預設值覆寫為適合其案例的設定。 被覆寫的前述預設值並未變更。

元件 cookie 預設
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 驗證 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 和更新版本提供下列 SameSite 支援:

  • 重新定義 SameSiteMode.None 發出 SameSite=None 的行為
  • 新增會省略 SameSite 屬性的新值 SameSiteMode.Unspecified
  • 所有 cookie API 都預設為 Unspecified。 某些使用 cookie 的元件會依據其案例設定更明確的值。 如需範例,請參閱上表。

在 ASP.NET Core 3.0 和更新版本中,SameSite 預設值已變更,以避免與不一致的用戶端預設值發生衝突。 下列 API 已將預設值從 SameSiteMode.Lax 變更為 -1,以避免針對下列 cookie 發出 SameSite 屬性:

歷程記錄和變更

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 應標示為 SecureNone 是要選擇退出的新項目。
  • 受到針對 ASP.NET Core 2.1、2.2 和 3.0 發出的修補檔支援。 ASP.NET Core 3.1 和更新版本有額外的 SameSite 支援。
  • 排定於 2020 年 2 月Chrome 依預設啟用。 瀏覽器於 2019 年開始移轉至此標準。

從 2016 SameSite 草稿標準變更為 2019 草稿標準後影響到的 API

支援舊版瀏覽器

2016 SameSite 標準強制將未知的值視為 SameSite=Strict 值。 從支援 2016 SameSite 標準的舊版瀏覽器存取的應用程式在取得值為 None 的 SameSite 屬性時可能會中斷。 如果 Web 應用程式預定要支援舊版瀏覽器,則必須實作瀏覽器偵測。 ASP.NET Core 不會實作瀏覽器偵測,因為 User-Agents 值非常不穩定,經常變更。 Microsoft.AspNetCore.CookiePolicy 中的擴充點允許插入 User-Agent 特定邏輯。

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 問題

與遠端站台互動的應用程式 (例如透過第三方登入者) 必須:

使用可選擇加入新 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 Projects 的 SameSite 更新

使用 Safari 進行測試

Safari 12 嚴格實作先前的草稿,若 cookie 中有新的 None 值,則會失敗。 透過本文件的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 進行測試

Firefox 對新標準的支援可在版本 68+ 上測試,方法是在具有功能旗標 network.cookie.sameSite.laxByDefaultabout:config 分頁上選擇加入。 目前為止並未回報任何舊版 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。

將 API 與 SameSite 搭配使用

HttpContext.Response.Cookies.Append 預設為 Unspecified,這表示沒有任何 SameSite 屬性新增至 cookie,且用戶端會使用其預設行為 (新瀏覽器為 Lax,舊瀏覽器為 None)。 下列程式碼說明如何將 cookie SameSite 值變更為 SameSiteMode.Lax

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

所有發出 cookie 的 ASP.NET Core 元件都會將前述預設值覆寫為適合其案例的設定。 被覆寫的前述預設值並未變更。

元件 cookie 預設
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 驗證 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

ASP.NET Core 3.1 和更新版本提供下列 SameSite 支援:

  • 重新定義 SameSiteMode.None 發出 SameSite=None 的行為
  • 新增會省略 SameSite 屬性的新值 SameSiteMode.Unspecified
  • 所有 cookie API 都預設為 Unspecified。 某些使用 cookie 的元件會依據其案例設定更明確的值。 如需範例,請參閱上表。

在 ASP.NET Core 3.0 和更新版本中,SameSite 預設值已變更,以避免與不一致的用戶端預設值發生衝突。 下列 API 已將預設值從 SameSiteMode.Lax 變更為 -1,以避免針對下列 cookie 發出 SameSite 屬性:

歷程記錄和變更

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 應標示為 SecureNone 是要選擇退出的新項目。
  • 受到針對 ASP.NET Core 2.1、2.2 和 3.0 發出的修補檔支援。 ASP.NET Core 3.1 有額外的 SameSite 支援。
  • 排定於 2020 年 2 月Chrome 依預設啟用。 瀏覽器於 2019 年開始移轉至此標準。

從 2016 SameSite 草稿標準變更為 2019 草稿標準後影響到的 API

支援舊版瀏覽器

2016 SameSite 標準強制將未知的值視為 SameSite=Strict 值。 從支援 2016 SameSite 標準的舊版瀏覽器存取的應用程式在取得值為 None 的 SameSite 屬性時可能會中斷。 如果 Web 應用程式預定要支援舊版瀏覽器,則必須實作瀏覽器偵測。 ASP.NET Core 不會實作瀏覽器偵測,因為 User-Agents 值非常不穩定,經常變更。 Microsoft.AspNetCore.CookiePolicy 中的擴充點允許插入 User-Agent 特定邏輯。

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 問題

與遠端站台互動的應用程式 (例如透過第三方登入者) 必須:

使用可選擇加入新 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 Projects 的 SameSite 更新

使用 Safari 進行測試

Safari 12 嚴格實作先前的草稿,若 cookie 中有新的 None 值,則會失敗。 透過本文件的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 進行測試

Firefox 對新標準的支援可在版本 68+ 上測試,方法是在具有功能旗標 network.cookie.sameSite.laxByDefaultabout:config 分頁上選擇加入。 目前為止並未回報任何舊版 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 的屬性」。 在此修補檔之後,SameSite 值若為 (SameSiteMode)(-1),則不會發出屬性。

表單驗證和工作階段狀態 cookie 的預設 SameSite 值已從 None 變更為 Lax

將 API 與 SameSite 搭配使用

HttpContext.Response.Cookies.Append 預設為 Unspecified,這表示沒有任何 SameSite 屬性新增至 cookie,且用戶端會使用其預設行為 (新瀏覽器為 Lax,舊瀏覽器為 None)。 下列程式碼說明如何將 cookie SameSite 值變更為 SameSiteMode.Lax

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

所有發出 cookie 的 ASP.NET Core 元件都會將前述預設值覆寫為適合其案例的設定。 被覆寫的前述預設值並未變更。

元件 cookie 預設
CookieBuilder SameSite Unspecified
Session SessionOptions.Cookie Lax
CookieTempDataProvider CookieTempDataProviderOptions.Cookie Lax
IAntiforgery AntiforgeryOptions.Cookie Strict
Cookie 驗證 CookieAuthenticationOptions.Cookie Lax
AddTwitter TwitterOptions.StateCookie Lax
RemoteAuthenticationHandler<TOptions> RemoteAuthenticationOptions.CorrelationCookie None
AddOpenIdConnect OpenIdConnectOptions.NonceCookie None
HttpContext.Response.Cookies.Append CookieOptions Unspecified

歷程記錄和變更

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 應標示為 SecureNone 是要選擇退出的新項目。
  • 受到針對 ASP.NET Core 2.1、2.2 和 3.0 發出的修補檔支援。 ASP.NET Core 3.1 有額外的 SameSite 支援。
  • 排定於 2020 年 2 月Chrome 依預設啟用。 瀏覽器於 2019 年開始移轉至此標準。

從 2016 SameSite 草稿標準變更為 2019 草稿標準後影響到的 API

支援舊版瀏覽器

2016 SameSite 標準強制將未知的值視為 SameSite=Strict 值。 從支援 2016 SameSite 標準的舊版瀏覽器存取的應用程式在取得值為 None 的 SameSite 屬性時可能會中斷。 如果 Web 應用程式預定要支援舊版瀏覽器,則必須實作瀏覽器偵測。 ASP.NET Core 不會實作瀏覽器偵測,因為 User-Agents 值非常不穩定,經常變更。 Microsoft.AspNetCore.CookiePolicy 中的擴充點允許插入 User-Agent 特定邏輯。

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 問題

與遠端站台互動的應用程式 (例如透過第三方登入者) 必須:

使用可選擇加入新 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 Projects 的 SameSite 更新

使用 Safari 進行測試

Safari 12 嚴格實作先前的草稿,若 cookie 中有新的 None 值,則會失敗。 透過本文件的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 進行測試

Firefox 對新標準的支援可在版本 68+ 上測試,方法是在具有功能旗標 network.cookie.sameSite.laxByDefaultabout:config 分頁上選擇加入。 目前為止並未回報任何舊版 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 範例