Cookie SameSite屬性介紹及其在ASP.NET專案中的應用
阿新 • • 發佈:2020-03-29
#### 一、Cookie SameSite屬性介紹
就像大家已經知道的,一旦設定Cookie之後,在Cookie失效之前瀏覽器會一直將這個Cookie在後續所有的請求中都傳回到Server端。我們的系統會利用Cookie這個特性做很多事情,但通常我們會在Cookie中存放加密的使用者身份,在Server端根據此身份檢驗使用者是否有許可權進行相應操作。
傳送Cookie時,以往瀏覽器並不檢測當前位址列上的域(Domain)是不是和這個Cookie所屬的域是否相同。惡意使用者會利用這個問題巧妙設計一個站點,誘導使用者點選從而造成跨站點請求偽造攻擊(CSRF)。
為了解決這個問題,[國際網際網路工程任務組(IETF)](https://ietf.org/about/)提出了一個SameSite的草稿標準,Chrome 51開始支援此功能,但從Chrome 80 Stable版本開始啟用一個較嚴格(Lax)的預設設定。
#### 二、什麼是跨站點請求偽造攻擊(Cross-Site Request Forgery Attack,CSRF)
CSRF攻擊簡單而言就是惡意使用者通過巧妙偽造請求從而盜用合法使用者的身份進行惡意操作。
比如你開發了一個非常厲害的系統,系統中某些操作只有特定的人登入之後才有許可權使用:
yourdomain.com/snap
````csharp
[Authorize("Thanos")]
[HttpPost]
public ActionResult Snap()
{
///dangerous, will destroy the world.
}
````
因為系統要檢驗身份和許可權,除非惡意使用者能破解登入系統以Thanos身份登入,否則是沒有辦法呼叫這個方法的。
但是惡意使用者可以偽造一個像下面這樣的頁面,惡意使用者通過發郵件或者通過跨站點指令碼攻擊(XSS)等方式誘導具有許可權的使用者點選頁面上的某些Button。如果具有許可權的使用者剛好已經登入,一旦點選按鈕,系統則會以這個使用者的身份觸發上面危險的操作Snap()。
malicioususer.com/fancypage
````html
...
...
````
當然,微軟 ASP.NET是通過[AntiForgeryToken](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.htmlhelper.antiforgerytoken)來解決這個問題,不過這個不是這篇blog討論的主題。
#### 三、Cookie的SameSite屬性
為了解決上面到的Cookie的安全問題,Chrome從版本51增加了一個新的Cookie屬性SameSite, 以控制Cookie是否能在跨站點的情況下傳送。
Cookie所屬的域名如果和瀏覽器位址列中的域名不一致,則認為是跨站點。另外,當你的站點被ifame嵌在第三方站點時也被認為是跨站點。
這個屬性有三個屬性值:
1. **None**
如果你需要在任意跨站點情況下都使用某個Cookie,則需要將這個Cookie的SameSite設定為None. 但這裡需要注意的是一定要同時設定Cookie的**Secure**,也就是需要使用https訪問時才能關閉SameSite功能. 如果沒有標明為secure, Chrome 80及以上會拒絕設定這個Cookie。
````html
set-cookie: samesite=test; path=/; secure; SameSite=None
````
2. **Strict**
故名思義,這是嚴格模式,就是在任何情況下都不允許跨站點發送Cookie。
這個設定顯然是可以解決上面所提到的CSRF問題。因為當訪問 malicioususer.com/fancypage 頁面時,當前域是 malicioususer.com, 但user點選button提交時的action是指向另外一個域 yourdomain.com,這是兩個不同的域,瀏覽器將不回傳yourdomain.com下面的Cookie。這會極大的提高我們系統的安全性。
但這個嚴格模式也限制了一些被認為是安全的連結操作,比如:
1. 你先登入了公司HR系統,假設該系統將所有Cookie的SameSite都設定為strict.
2. 你用Web郵件系統收到了要求你到HR系統做審批操作的郵件,這封郵件帶了一個link,直接連結到HR系統中審批的頁面;
3. 你點選這個link,但因為Cookie被設定為Strict模式,當到達審批頁面時,HR系統沒有收到任何Cookie,這時會認為你沒有登入,而直接跳轉到登入頁面。在要求不是非常嚴格的情形下,可以認為這不是我們所期望的行為。因為只是跳到連結指向的頁面並不是像POST操作修改資料。這需要通過下面的Lax屬性解決這個問題。
3. **Lax**
Lax是比Strict稍寬鬆的模式,如果我們要允許跨站點連結傳Cookie或FORM用GET Method提交時跨站點傳Cookie, 則可以將這些Cookie的SameSite設定為Lax. Lax在Chrome 80成為預設設定,Lax既防止了CSRF也確保了正常的跨站點連結,是適合大多數站點的,可以解決上面HR系統安全中提到問題。
如果你的站點需要被iframe巢狀在第三方站點,這時你還是需要將Cookie設定為None。
這裡也想到一點是,如果你的MVC Action只期望接受POST方法,那麼一定要加上HttpPost Attribute,以避免造成意外的安全問題。
#### 四、瀏覽器相容性
如下圖示目前主流瀏覽器都已經支援SameSite,雖然 IE 11不支援,但我測試之後發現這個Cookie本身還是沒有丟失,只是缺失了安全保護功能。
https://developer.mozilla.org/en-US/docs/Web/HTTP/headers/Set-Cookie#Browser_compatibility
![Browser Compabibility](https://images.cnblogs.com/cnblogs_com/wxx/1683291/o_200328144905SameSite-Browser-Compatibility.png)
#### 五、如何修改ASP.NET程式
下面總結的步驟是適用於基於ASP.NET開發的系統。微軟官方白皮書對這些屬性設定做了詳細的說明,也可以參考[官方白皮書](https://docs.microsoft.com/en-us/aspnet/samesite/system-web-samesite#sob)。
1. 安裝 .NET Framework 4.7.2 或4.8, 需要安裝在開發電腦和伺服器上。
2. 安裝 Windows 2019/11/19累積更新補丁,請見[KB Articles that support SameSite in .NET Framework](https://docs.microsoft.com/en-us/aspnet/samesite/kbs-samesite),需要安裝在開發電腦和伺服器上。
3. 在Chrome位址列輸入: **chrome://flags/**, 將下面兩項設定為Enabled。開啟這兩項設定是因為不是所有的Chrome都預設啟用了這兩項設定,Chrome只是在逐漸將這兩項開啟到Chrome的user. 所以開發時為了重現問題,最好是顯式開啟。
chrome://flags/#same-site-by-default-cookies
chrome://flags/#cookies-without-same-site-must-be-secure
![Enable Chrome SameSite explicitly](https://images.cnblogs.com/cnblogs_com/wxx/1683291/o_200328144858Chrome-Enable-SameSite.png)
4. 修改專案檔案屬性, Target framework 4.7.2 或4.8。
![target .net 4.7.2 or above](https://images.cnblogs.com/cnblogs_com/wxx/1683291/o_200328144915target-dot-net472-above.png)
5. 根據需要修改web.config對Cookie的SameSite設定。
````