在 ASP.NET 中使用SameSite Cookie

作者:Rick Anderson

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

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

SameSite=Lax 設定適用於大部分的應用程式 Cookie。 某些形式的驗證,例如 OpenID Connect (OIDC) 和 WS-Federation 預設為 POST 型重新導向。 POST 型重新導向會觸發 SameSite 瀏覽器保護,因此這些元件會停用 SameSite。 由於要求流程的差異,因此不會影響大部分 的 OAuth 登入。

發出 Cookie 的每個 ASP.NET 元件都必須決定 SameSite 是否適當。

如需安裝 2019 .Net SameSite 更新之後應用程式的問題,請參閱 已知問題

在 ASP.NET 4.7.2 和 4.8 中使用 SameSite

.Net 4.7.2 和 4.8 支援 SameSite 的 2019 草稿標準,自 2019 年 12 月的更新發行以來。 開發人員可以使用 HttpCookie.SameSite 屬性,以程式設計方式控制 SameSite 標頭的值。 將 SameSite 屬性設定為 StrictLaxNone 會導致使用 Cookie 在網路上寫入這些值。 將它設定為 (SameSiteMode)(-1) 等於 ,表示網路上不應包含任何 SameSite 標頭與 Cookie。 組態檔中的 HttpCookie.Secure 屬性或 'requireSSL' 可用來將 Cookie 標示為 Secure 或不要。

新的 HttpCookie 實體預設為 SameSite=(SameSiteMode)(-1)Secure=false。 您可以在組態區段中覆 system.web/httpCookies 寫這些預設值,其中字串 "Unspecified" 是 的 (SameSiteMode)(-1)易記僅限組態語法:

<configuration>
 <system.web>
  <httpCookies sameSite="[Strict|Lax|None|Unspecified]" requireSSL="[true|false]" />
 <system.web>
<configuration>

ASP.Net 也會針對這些功能發出自己的四個特定 Cookie:匿名驗證、窗體驗證、會話狀態和角色管理。 這些在運行時間中取得的 Cookie 實例可以使用 和 Secure 屬性來操作SameSite,就像任何其他 HttpCookie 實例一樣。 不過,由於 SameSite 標準的修補出現,這四個功能 Cookie 的組態選項不一致。 相關組態區段和屬性的預設值如下所示。 如果功能沒有 SameSiteSecure 相關的屬性,此功能將會回復在上述章節中 system.web/httpCookies 設定的預設值。

<configuration>
 <system.web>
  <anonymousIdentification cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
  <authentication>
   <forms cookieSameSite="Lax" requireSSL="false" />
  </authentication>
  <sessionState cookieSameSite="Lax" /> <!-- No config attribute for Secure -->
  <roleManager cookieRequireSSL="false" /> <!-- No config attribute for SameSite -->
 <system.web>
<configuration>

注意:「未指定」目前僅適用於 system.web/httpCookies@sameSite 。 我們希望在未來的更新中,將類似的語法新增至先前顯示的cookieSameSite屬性。 程序代碼中的設定 (SameSiteMode)(-1) 仍適用於這些 Cookie 的實例。*

如果您要以英文以外的語言閱讀,如果您想要以原生語言查看程式代碼批注,請在此 GitHub 討論問題 中告訴我們。

將 .NET 應用程式複位目標

若要以 .NET 4.7.2 或更新版本為目標:

  • 請確定 web.config 包含下列專案:

    <system.web>
      <compilation targetFramework="4.7.2"/>
      <httpRuntime targetFramework="4.7.2"/>
    </system.web>
    
    
  • 確認項目檔包含正確的 TargetFrameworkVersion

    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    

    .NET 移轉指南有更多詳細數據。

  • 確認專案中的 NuGet 套件是以正確的架構版本為目標。 您可以檢查 packages.config 檔案來驗證正確的架構版本,例如:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net472" />
      <package id="Microsoft.ApplicationInsights" version="2.4.0" targetFramework="net451" />
    </packages>
    

    在上述 packages.config 檔案中 Microsoft.ApplicationInsights ,封裝:

    • 以 .NET 4.5.1 為目標。
    • targetFramework如果以架構目標為目標的已更新套件存在,則應將其屬性更新為 net472

4.7.2 之前的 .NET 版本

Microsoft 不支持低於 4.7.2 的 .NET 版本來撰寫相同的網站 Cookie 屬性。 我們找不到可靠的方法:

  • 請確定屬性是以瀏覽器版本正確撰寫。
  • 攔截並調整舊版架構上的驗證和會話 Cookie。

12 月修補程式行為變更

.NET Framework 的特定行為變更是屬性解譯None值的方式SameSite

  • 在修補之前,其 None 值如下:
    • 完全不要發出 屬性。
  • 修補程序之後:
    • 的值 None 表示「發出值為 None」的屬性。
    • SameSite的值(SameSiteMode)(-1)會導致屬性不會發出。

窗體驗證和工作階段狀態 Cookie 的預設 SameSite 值已從 None 變更為 Lax

變更對瀏覽器的影響摘要

如果您安裝修補程式,並使用 發出 Cookie SameSite.None,將會發生兩件事之一:

  • Chrome v80 會根據新的實作來處理此 Cookie,而不會對 Cookie 強制執行相同的網站限制。
  • 任何尚未更新以支援新實作的瀏覽器都會遵循舊實作。 舊實作顯示:
    • 如果您看到不瞭解的值,請忽略它,並切換至嚴格的相同網站限制。

因此,應用程式會在 Chrome 中中斷,或您在許多其他位置中斷。

歷程記錄和變更

SameSite 支援最初是使用 2016 草稿標準在 .NET 4.7.2 中實作。

Windows 2019 年 11 月 19 日更新了 2016 年 11 月 19 日從 2016 標準更新為 2019 標準。 其他版本的 Windows 即將推出的其他更新。 如需詳細資訊,請參閱 .NET Framework 中支援SameSite的知識庫文章

SameSite 規格的 2019 草稿:

  • 與 2016 草稿 不相容 。 如需詳細資訊,請參閱本文件中 支援舊版瀏覽器
  • 指定預設會將 Cookie 視為 SameSite=Lax
  • 指定明確判斷提示 SameSite=None 以啟用跨網站傳遞的 Cookie 也應該標示為 Secure
  • 如上面所列的 KB 中所述,所發出的修補程序支援。
  • 排定在 2020 年 2 月Chrome 預設為啟用。 瀏覽器在 2019 年開始移至此標準。

已知問題

因為 2016 和 2019 草稿規格不相容,所以 2019 年 11 月 .Net Framework 更新引進了一些可能中斷的變更。

  • 會話狀態和窗體驗證 Cookie 現在會寫入網路,而不是 Lax 未指定。
    • 雖然大部分的應用程式都使用 SameSite=Lax Cookie,但跨網站或應用程式 iframe 張貼的應用程式可能會發現其會話狀態或表單授權 Cookie 未如預期般使用。 若要解決此問題,請 cookieSameSite 如先前所述,變更適當組態一節中的值。
  • 在程式代碼或組態中明確設定 SameSite=None 的 HttpCookies 現在會以 Cookie 寫入該值,但先前已省略此值。 這可能會導致舊版瀏覽器只支援 2016 草稿標準的問題。
    • 以支援使用 SameSite=None Cookie 之 2019 草稿標準的瀏覽器為目標時,請記得同時標示它們 Secure ,否則可能無法辨識它們。
    • 若要還原為未寫入 SameSite=None的 2016 行為,請使用應用程式設定 aspnet:SupressSameSiteNone=true。 請注意,這會套用至應用程式中的所有 HttpCookies。

如需 Azure App 服務 如何在 .Net 4.7.2 應用程式中設定 SameSite 行為的相關信息,請參閱 Azure App 服務 — SameSite Cookie 處理和 .NET Framework 4.7.2 修補程式

支援舊版瀏覽器

2016 SameSite 標準規定未知的值必須視為 SameSite=Strict 值。 從支援 2016 SameSite 標準的舊版瀏覽器存取的應用程式可能會在取得值為 None的 SameSite 屬性時中斷。 如果 Web 應用程式想要支援較舊的瀏覽器,則必須實作瀏覽器偵測。 ASP.NET 不會實作瀏覽器偵測,因為 User-Agents 值高度變動且經常變更。

Microsoft 修正問題的方法,是協助您實作瀏覽器偵測元件,以在瀏覽器已知不支援時從 Cookie 移除 sameSite=None 屬性。 Google 的建議是發出雙 Cookie、一個具有新屬性,另一個沒有 屬性。 不過,我們考慮Google的建議有限。 某些瀏覽器,特別是行動瀏覽器對於網站或功能變數名稱可以傳送的 Cookie 數目有非常小的限制。 傳送多個 Cookie,特別是驗證 Cookie 之類的大型 Cookie 可以非常快速地達到行動瀏覽器限制,導致難以診斷和修正的應用程式失敗。 此外,架構還有大型的第三方程式代碼和元件生態系統,可能無法更新為使用雙 Cookie 方法。

此 GitHub 存放庫中範例專案中所使用的瀏覽器偵測程式代碼包含在兩個檔案中

這些偵測是我們所看到的最常見瀏覽器代理程式,可支援 2016 標準,而且必須完全移除屬性。 這不是完整的實作:

  • 您的應用程式可能會看到我們的測試網站沒有的瀏覽器。
  • 您應該準備好視您的環境需要新增偵測。

線上偵測的方式會根據您所使用的 .NET 版本和 Web 架構而有所不同。 您可以在 HttpCookie 呼叫網站上呼叫下列程式代碼:

private void CheckSameSite(HttpContext httpContext, HttpCookie cookie)
{
    if (cookie.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.UserAgent;
        if (BrowserDetection.DisallowsSameSiteNone(userAgent))
        {
            cookie.SameSite = (SameSiteMode)(-1);
        }
    }
}

請參閱下列 ASP.NET 4.7.2 SameSite Cookie 主題:

確保您的網站重新導向至 HTTPS

對於 ASP.NET 4.x、WebForms 和 MVC,IIS 的 URL 重寫 功能可用來將所有要求重新導向至 HTTPS。 下列 XML 顯示範例規則:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Redirect to https" stopProcessing="true">
          <match url="(.*)"/>
          <conditions>
            <add input="{HTTPS}" pattern="Off"/>
            <add input="{REQUEST_METHOD}" pattern="^get$|^head$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent"/>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

IIS URL 重寫 的內部部署安裝中,是可能需要安裝的選擇性功能。

測試 SameSite 問題的應用程式

您必須使用您支援的瀏覽器測試應用程式,並流覽涉及 Cookie 的案例。 Cookie 案例通常涉及

  • 登入表單
  • 外部登入機制,例如 Facebook、Azure AD、OAuth 和 OIDC
  • 接受來自其他網站的要求的頁面
  • 應用程式中設計成內嵌在 iframe 中的頁面

您應該檢查是否已在應用程式中正確建立、保存和刪除 Cookie。

與遠端網站互動的應用程式,例如透過第三方登入需要:

使用可加入加入新 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 匯報

使用 Chrome 80+ 進行測試

下載 支援其新屬性的 Chrome 版本。 在撰寫本文時,目前的版本是 Chrome 80。 Chrome 80 需要啟用 旗標 chrome://flags/#same-site-by-default-cookies 才能使用新的行為。 您也應該啟用 (chrome://flags/#cookies-without-same-site-must-be-secure) ,以測試未啟用相同Site 屬性的 Cookie 即將推出的行為。 Chrome 80 已設為目標,可讓參數將不含 屬性的 Cookie 視為 SameSite=Lax,但具有特定要求的計時寬限期。 若要停用定時寬限期 Chrome 80,可以使用下列命令行自變數來啟動:

--enable-features=SameSiteDefaultChecksMethodRigorously

Chrome 80 在瀏覽器控制台中有關於遺漏 sameSite 屬性的警告訊息。 使用 F12 開啟瀏覽器主控台。

使用Safari進行測試

Safari 12 嚴格實作先前的草稿,並在新 None 值位於Cookie時失敗。 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 進行測試

您可以藉由選擇在具有功能旗network.cookie.sameSite.laxByDefault標的頁面上選擇加入 about:config ,以測試新標準的 Firefox 支援。 舊版 Firefox 的相容性問題尚未回報。

使用 Edge (舊版) 瀏覽器進行測試

Edge 支援舊的 SameSite 標準。 Edge 44+ 版與新標準沒有任何已知的相容性問題。

使用 Edge (Chromium) 進行測試

SameSite 旗標是在頁面上設定 edge://flags/#same-site-by-default-cookies 。 Edge Chromium 未發現任何相容性問題。

使用電子進行測試

Electron 的版本包括舊版 Chromium。 例如,Teams 所使用的電子版本 Chromium 66,其表現較舊的行為。 您必須使用您產品所使用的電子版本來執行自己的相容性測試。 請參閱 支援舊版瀏覽器

還原 SameSite 修補程式

您可以將 .NET Framework 應用程式中更新的 sameSite 行為還原為其先前的行為,其中相同Site 屬性未針對 的值None發出,並將驗證和會話 Cookie 還原為不發出值。 這應該視為 非常暫時的修正程序,因為 Chrome 變更會中斷任何外部 POST 要求,或使用支援標準變更的瀏覽器來驗證使用者。

還原 .NET 4.7.2 行為

更新 web.config 以包含下列組態設定:

<configuration> 
  <appSettings>
    <add key="aspnet:SuppressSameSiteNone" value="true" />
  </appSettings>
 
  <system.web> 
    <authentication> 
      <forms cookieSameSite="None" /> 
    </authentication> 
    <sessionState cookieSameSite="None" /> 
  </system.web> 
</configuration>

其他資源