使用IdentityServer出現過SameSite Cookie這個問題嗎?

使用IdentityServer出現過SameSite Cookie這個問題嗎?

如果您為 Web 應用程式和驗證伺服器使用不同的網域,那麼 Chrome 中的這種變更很可能會破壞部分使用者的會話體驗

最後更新 2022/4/28 上午6:47
Sebastian Gingter
預計閱讀 16 分鐘
分類
.NET
標籤
.NET C# 驗證 同源

原文作者:Sebastian Gingter

原文連結:https://www.thinktecture.com/en/identityserver/prepare-your-identityserver/

譯者:沙漠盡頭的狼

譯文連結:https://dotnet9.com/2022/04/How-To-Prepare-Your-IdentityServer-For-Chromes-SameSite-Cookie-Changes-And-How-To-Deal-With-Safari-Nevertheless

本文是作者 2019 年的一篇分享,裡面的一些觀點和使用的技術,對我們現在的開發依然有效,建議查看原文閱讀,對本文翻譯如有疑問,歡迎提 PR

首先,好消息:Google 將在 2020 年 2 月發布 Chrome 80 時,包括 Google 實施的「漸進式更好的 Cookie」(Incrementally better Cookies),這將使網路成為一個更安全的地方,並有助於確保使用者獲得更好的隱私(站長註:現在是 2022 年 4 月 28 號,Chrome 已經發布了多個更新版本)。

壞消息是,這個新實現是瀏覽器決定如何向伺服器發送 cookie 的重大變化。首先,如果您為 Web 應用程式和身分驗證伺服器使用單獨的網域,那麼 Chrome 中的這種更改很可能會破壞部分使用者的會話體驗。第二個問題是它還可能使您的部分使用者無法再次正確登出您的系統。

1. 首先,這個 SameSite 是關於什麼的?

Web 是一個非常開放的平台:Cookie 是在大約 20 年前設計的,以及 2011 年在 RFC 6265中重新審視該設計時,跨站請求偽造 (CSRF) 攻擊和過度使用者追蹤還不是什麼大事。

簡而言之,正常的 Cookie 規範說,如果為特定網域設定了 Cookie,它將在瀏覽器發出的每個請求時帶上 Cookie 發送到該網域。無論您是否直接導航到該網域,如果瀏覽器只是從該網域載入資源(即圖像),向其發送 POST 請求或將其其中的一部分嵌入到 iframe 中。但也許對於後一種可能性,您不希望瀏覽器自動將使用者會話 Cookie 發送到您的伺服器,因為這將允許任何網站在該使用者的情境中執行針對您的伺服器的請求的 JavaScript,而不會引起他們的注意。

為了防止這種情況發生, SameSite cookie 規範 是在 2016 年起草的。它讓您可以更好地控制何時應該或不應該發送 cookie:當您設定 cookie 時,您現在可以為每個 cookie 明確指定瀏覽器何時應將其加入請求。為此,當瀏覽器位於您自己的網域時,它引入了同站點 cookie 的概念,而當瀏覽器在不同網域中導航但向您的網域發送請求時,它引入了跨站點 cookie 的概念。

為了向後相容,相同站點 cookie 的預設設定並沒有改變先前的行為。您必須選擇加入該新功能並明確設定您的 cookie SameSite=LaxSameSite=Strict 使其更安全。這已在 .NET Framework(包括.NET CORE) 和所有常見瀏覽器中實現。 Lax 意味著,cookie 將在初始導航時發送到伺服器, Strict 意味著 cookie 只會在您已經在該網域上時發送(即初始導航後的第二個請求)。

遺憾的是,這項新功能的採用速度很慢(根據 2019 年 3 月 Chrome 的遙測資料 【來源 】,全球範圍內 Chrome 上處理的所有 cookie 中只有 0.1% 使用 SameSite 標誌)。

Google 決定推動採用該功能。為了強制執行,他們決定更改世界上最常用的瀏覽器的預設行為:Chrome 80 將 必須 指定一個新的設定 SameSite=None 來保留處理 cookie 的舊方式,如果您像舊規範建議的那樣省略 SameSite 欄位,它將 cookie 視為使用 SameSite=Lax

請注意: 該設定 SameSite=None 僅在 cookie 也被標記為 Secure 並需要 HTTPS 連線時才有效。

更新: 如果您想了解有關 SameSite cookie 的更多背景資訊,有一篇包含 所有細節的新文章

2. 這對我有影響嗎?如果是,怎麼做?

如果您有一個單頁面 Web 應用程式 (SPA),它針對託管在不同網域上的身分提供者(IdP,例如 IdentityServer 4)進行身分驗證,並且該應用程式使用所謂的靜默令牌重新整理,您就會受到影響。

登入 IdP 時,它會為您的使用者設定一個會話 cookie,該 cookie 來自 IdP 網域。在身分驗證流程結束時,來自不同網域的應用程式會收到某種存取令牌,這些令牌通常不會很長時間。當該令牌過期時,應用程式將無法再存取資源伺服器 (API),如果每次發生這種情況時使用者都必須重新登入,這將是非常糟糕的使用者體驗。

為防止這種情況,您可以使用靜默令牌重新整理。在這種情況下,應用程式會建立一個使用者不可見的 iframe,並在該 iframe 中再次啟動身分驗證程序。IdP 的網站在 iframe 中載入,如果瀏覽器沿 IdP 發送會話 cookie,則識別使用者並發出新令牌。

現在 iframe 存在於託管在應用程式網域中的 SPA 中,其內容來自 IdP 網域。如果 cookie 明確指出 SameSite=None,Chrome 80 只會將該 cookie 從 iframe 發送到 IdP,這被認為是跨站點請求。 如果不是這種情況,您的靜默令牌重新整理將在 2 月 Chrome 80 發佈時中斷。

還有其他情況可能會給您帶來問題:首先,如果您在 Web 應用程式或網站中嵌入源自另一個網域的元素,例如影片的自動播放設定,並且這些需要 cookie 才能正常執行,這些也會需要設定 SameSite 策略。如果您的應用程式需要從依賴於 cookie 身分驗證的瀏覽器請求第 3 方 API,這同樣適用。

注意: 顯然您只能更改您自己的伺服器關於 cookie 設定的 cookie 行為。如果您碰巧使用了不受您控制的其他網域中的元素,您需要聯絡第 3 方,並在出現問題時要求他們更改 cookie。

3. 好的,我將更改我的程式碼並將 SameSite 設定為 None。我現在可以了,對吧?

不幸的是,Safari 有一個「錯誤」。此錯誤導致 Safari 無法將新引入的值 None 識別為 SameSite 設定的有效值。當 Safari 遇到無效值時,它會將 SameSite=Strict 當作已指定的設定,並且不會將會話 cookie 發送到 IdP。此錯誤已在 iOS 13 和 macOS 10.15 Catalina 上的 Safari 13 中修復,但不會向後移植到 macOS 10.14 Mojave 和 iOS 12,它們仍然擁有非常大的使用者群。

所以,我們現在陷入了兩難境地:要嘛我們忽略 SameSite 策略,我們的 Chrome 使用者無法進行靜默重新整理,要嘛我們設定 SameSite=None 並鎖定 iPhone、iPad 和 Mac 使用者無法更新,或者舊裝置無法更新到最新版本的 iOS 和 macOS。

4. 有沒有辦法確定我受到影響?

幸運的是,是的。如果您已經設定 SameSite=None,您可能已經注意到您的應用程式或網站在 iOS 12 和 macOS 10.4 上的 Safari 中無法正常運作。如果沒有,請確保在這些版本的 Safari 中測試您的應用程式或網站。

如果您根本不設定 SameSite 值,您只需在 Chrome 中開啟您的應用程式並開啟開發人員工具即可。您將看到以下警告:

A cookie associated with a cross-site resource at {cookie domain} was set without the `SameSite` attribute.
A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`.
You can review cookies in developer tools under Application>Storage>Cookies and see more details at
https://www.chromestatus.com/feature/5088147346030592 and
https://www.chromestatus.com/feature/5633521622188032.

如果您已經設定 SameSite=None 但忘了設定 Secure 標誌,您將收到以下警告:

A cookie associated with a resource at {cookie domain} was set with `SameSite=None` but without `Secure`.
A future release of Chrome will only deliver cookies marked `SameSite=None` if they are also marked `Secure`.
You can review cookies in developer tools under Application>Storage>Cookies and
see more details at https://www.chromestatus.com/feature/5633521622188032.

5. 那麼,我該如何真正解決這個問題?我需要 Chrome 和 Safari 正常使用。

我們,也就是我的同事 Boris Wilhelms 和我自己,對該主題進行了一些研究,並找到且驗證了解決方案。微軟的 Barry Dorrans 也有一篇 關於這個問題的好博文。該解決方案並不美觀,遺憾的是需要在伺服器端進行瀏覽器嗅探,但這是一個簡單的解決方案,在過去的幾週裡,我們已經在我們的幾個客戶專案中成功實現了這一點。

要解決這個問題,我們首先需要確保需要透過跨站點請求傳輸的 cookie(例如我們的會話 cookie)設定為 SameSite=NoneSecure。我們需要在專案程式碼中找到該 cookie 的選項並進行相應調整。這解決了 Chrome 的問題並引入了 Safari 問題。

然後我們將以下類別和程式碼片段加入到專案中。這會在 ASP.NET Core Web 應用程式中加入和設定 cookie 策略。此策略將檢查是否設定了 cookie 為 SameSite=None 。如果是這種情況,它將檢查瀏覽器的使用者代理,並確定這是否是一個瀏覽器的設定有問題,比如我們受影響的 Safari 版本。如果也是這種情況,它會將 cookies SameSite 值設定為unspecified(未指定),這反過來將完全阻止設定 SameSite,從而為這些瀏覽器重新建立當前預設行為。

請注意: 此處提供的解決方案適用於 .NET Core。對於完整的基於 .NET Framework 的專案,您需要查看Barry Dorran 的文章中指定的版本之一 。

5.1 要加入到專案中的類別

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace Microsoft.Extensions.DependencyInjection
{
   public static class SameSiteCookiesServiceCollectionExtensions
   {
      /// <summary>
      /// -1 defines the unspecified value, which tells ASPNET Core to NOT
      /// send the SameSite attribute. With ASPNET Core 3.1 the
      /// <seealso cref="SameSiteMode" /> enum will have a definition for
      /// Unspecified.
      /// </summary>
      private const SameSiteMode Unspecified = (SameSiteMode) (-1);

      /// <summary>
      /// Configures a cookie policy to properly set the SameSite attribute
      /// for Browsers that handle unknown values as Strict. Ensure that you
      /// add the <seealso cref="Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware" />
      /// into the pipeline before sending any cookies!
      /// </summary>
      /// <remarks>
      /// Minimum ASPNET Core Version required for this code:
      ///   - 2.1.14
      ///   - 2.2.8
      ///   - 3.0.1
      ///   - 3.1.0-preview1
      /// Starting with version 80 of Chrome (to be released in February 2020)
      /// cookies with NO SameSite attribute are treated as SameSite=Lax.
      /// In order to always get the cookies send they need to be set to
      /// SameSite=None. But since the current standard only defines Lax and
      /// Strict as valid values there are some browsers that treat invalid
      /// values as SameSite=Strict. We therefore need to check the browser
      /// and either send SameSite=None or prevent the sending of SameSite=None.
      /// Relevant links:
      /// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1
      /// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
      /// - https://www.chromium.org/updates/same-site
      /// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
      /// - https://bugs.webkit.org/show_bug.cgi?id=198181
      /// </remarks>
      /// <param name="services">The service collection to register <see cref="CookiePolicyOptions" /> into.</param>
      /// <returns>The modified <see cref="IServiceCollection" />.</returns>
      public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services)
      {
         services.Configure<CookiePolicyOptions>(options =>
         {
            options.MinimumSameSitePolicy = Unspecified;
            options.OnAppendCookie = cookieContext =>
               CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
            options.OnDeleteCookie = cookieContext =>
               CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
         });

         return services;
      }

      private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
      {
         if (options.SameSite == SameSiteMode.None)
         {
            var userAgent = httpContext.Request.Headers["User-Agent"].ToString();

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

      /// <summary>
      /// Checks if the UserAgent is known to interpret an unknown value as Strict.
      /// For those the <see cref="CookieOptions.SameSite" /> property should be
      /// set to <see cref="Unspecified" />.
      /// </summary>
      /// <remarks>
      /// This code is taken from Microsoft:
      /// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
      /// </remarks>
      /// <param name="userAgent">The user agent string to check.</param>
      /// <returns>Whether the specified user agent (browser) accepts SameSite=None or not.</returns>
      private static bool DisallowsSameSiteNone(string userAgent)
      {
         // 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.
         // Notes from Thinktecture:
         // Regarding https://caniuse.com/#search=samesite iOS versions lower
         // than 12 are not supporting SameSite at all. Starting with version 13
         // unknown values are NOT treated as strict anymore. Therefore we only
         // need to check version 12.
         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.
         // Notes from Thinktecture:
         // Regarding https://caniuse.com/#search=samesite MacOS X versions lower
         // than 10.14 are not supporting SameSite at all. Starting with version
         // 10.15 unknown values are NOT treated as strict anymore. Therefore we
         // only need to check version 10.14.
         if (userAgent.Contains("Safari")
            && userAgent.Contains("Macintosh; Intel Mac OS X 10_14")
            && userAgent.Contains("Version/"))
         {
            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.
         // Notes from Thinktecture:
         // We can not validate this assumption, but we trust Microsofts
         // evaluation. And overall not sending a SameSite value equals to the same
         // behavior as SameSite=None for these old versions anyways.
         if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
         {
            return true;
         }

         return false;
      }
   }
}

要使用此 cookie 策略,您需要將以下內容加入到您的啟動程式碼中:

public void ConfigureServices(IServiceCollection services)
{
   // Add this
   services.ConfigureNonBreakingSameSiteCookies();
}

public void Configure(IApplicationBuilder app)
{
   // Add this before any other middleware that might write cookies
   app.UseCookiePolicy();

   // This will write cookies, so make sure it's after the cookie policy
   app.UseAuthentication();
}

6. 好的。我現在完成了嗎?

除了徹底的測試,特別是在 Chrome 79 中啟用了「預設 cookie 的 SameSite」標誌以及 macOS 和 iOS 上受影響的 Safari 版本,是的,你現在應該沒事了。要在 Chrome 79 中進行測試,請導航到 chrome://flags、搜尋 samesite 並啟用該 SameSite by default cookies 標誌。重新啟動瀏覽器,您可以立即測試即將發生的更改。

嚴肅的說:確保您的靜默重新整理 - 或者通常是需要 cookie 的跨站點請求 - 仍然可以在這些裝置和瀏覽器上執行。

7. 我不能簡單地等待我的身分驗證伺服器供應商為我解決這個問題嗎?

這是不太可能的。在我們這裡的具體範例中,實際上管理 cookie 的不是 IdentityServer 本身。IdentityServer 依賴於 ASP.NET Core 框架的內建身分驗證系統,這是管理會話 cookie 的地方。雖然 ASP.NET Core 框架已更新以支援新 SameSiteNone 和技術設定 Unspecified (不發送 SameSite ), 但微軟表示 他們不能直接在 ASP.NET Core 中引入使用者代理嗅探。所以這真的取決於你和現有的專案。

8. 總結

Chrome 將很快(2020 年 2 月)更改其處理 cookie 的預設行為。將來,它將預設 SameSite 被明確設定為None標誌 和 Secure 標誌設定,以允許將 cookie 加入到某些跨站點請求。如果你這樣做,常見版本的 Safari 就會對此感到厭煩。

為確保所有瀏覽器都滿意,您將所有受影響的 cookie 設定為 SecureSameSite=None,然後加入一個 cookie 策略(如上所示的程式碼),該策略可以覆蓋這些設定並再次為無法對 None 正確解釋該值的瀏覽器刪除SameSite標誌。

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/2/7

AOT使用經驗總結

從專案建立伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 AOT 發布測試。

繼續閱讀