前言
最近使用next.js來開發前端網站,在登錄環節發現cookie的存儲和跨域存在問題,一直沒弄懂cookie的原理,看了網上好多大佬的文章,大有收穫分享給大家。
Cookie介紹
HTTP cookie(Web cookie,瀏覽器 cookie)是服務器發送到用户 Web 瀏覽器的一小段數據。瀏覽器可能會存儲 cookie 並將其與稍後的請求一起發送回同一服務器。通常,HTTP cookie 用於判斷兩個請求是否來自同一個瀏覽器——例如,讓用户保持登錄狀態。它為無狀態HTTP 協議 記住有狀態信息。
Cookies主要用於三個目的:
1、會話管理:如登錄、購物車、遊戲分數或服務器應記住的任何其他內容
2、個性化:如用户偏好、主題和其他設置
3、追蹤:如記錄和分析用户行為
1、創建 cookie
接收到 HTTP 請求後,服務器可以發送一個或多個Set-Cookie響應頭。瀏覽器通常存儲 cookie 並將其與Cookie HTTP 標頭內的同一服務器的請求一起發送。您可以指定不應發送 cookie 的過期日期或時間段。您還可以對特定域和路徑設置附加限制,以限制 cookie 的發送位置。
Set-Cookie和Header Cookie
HTTP 響應(Response)標頭(Header)的Set-Cookie作用:從服務器發送cookie到用户代理。
一個簡單的 cookie 設置如下:
Set-Cookie: <cookie-name>=<cookie-value>
這指示服務器發送標頭告訴客户端存儲一對cookie:
HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
然後,對於服務器的每個後續請求,瀏覽器都會使用標頭將所有先前存儲的 cookie 帶回服務器Cookie。
GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
限制訪問 Cookie
有兩種方法可以確保 Cookie 被安全發送,並且不會被意外的參與者或腳本訪問:Secure 屬性和 HttpOnly 屬性。
標記為 Secure 的 Cookie 只應通過被 HTTPS 協議加密過的請求發送給服務端。它永遠不會使用不安全的 HTTP 發送(本地主機除外),這意味着中間人攻擊者無法輕鬆訪問它。不安全的站點(在 URL 中帶有 http:)無法使用 Secure 屬性設置 cookie。但是,Secure 不會阻止對 cookie 中敏感信息的訪問。例如,有權訪問客户端硬盤(或,如果未設置 HttpOnly 屬性,則為 JavaScript)的人可以讀取和修改它。
JavaScript Document.cookie API 無法訪問帶有 HttpOnly 屬性的 cookie;此類 Cookie 僅作用於服務器。例如,持久化服務器端會話的 Cookie 不需要對 JavaScript 可用,而應具有 HttpOnly 屬性。此預防措施有助於緩解跨站點腳本(XSS) (en-US)攻擊。
2、Cookie 的生命週期
cookie 的生命週期可以通過兩種方式定義:
1、當前會話結束時:會刪除會話cookie。(瀏覽器定義“當前會話”何時結束,一些瀏覽器在重啓時使用會話恢復。這可能會導致會話 cookie 無限期地持續)
2、永久cookie:在Expires屬性指定的日期或屬性指定的一段時間後刪除Max-Age。
例如:
Set-Cookie: id=a3fWa; Expires=Thu, 31 Oct 2021 07:28:00 GMT;
注意:
1、當您設置Expires日期和時間時,它們與設置 cookie 的客户端相關,而不是服務器。
2、要刪除Cookie,需要將Max-Age設置為0,並且將Cookie的值設置為null。不要將Max-Age指令值設置為-1負數。否則,瀏覽器會將其視為會話cookie。
3、如果您的站點對用户進行身份驗證,它應該重新生成並重新發送會話cookie,即使是已經存在的,只要用户進行身份驗證。這種方法有助於防止會話固定攻擊,第三方可以重用用户的會話。
3、限制對 cookie 的訪問
您可以通過以下兩種方式之一確保 cookie 安全發送,並且不會被非預期方或腳本訪問:
1、Secure屬性
2、HttpOnly屬性。
帶有該Secure屬性的 cookie 僅通過 HTTPS 協議通過加密請求發送到服務器。它永遠不會使用不安全的 HTTP發送(本地主機(localhost)除外),這意味着中間人攻擊者無法輕鬆訪問它。不安全的站點(http:在 URL 中帶有)無法使用該Secure屬性設置 cookie。但是,不要假設這會Secure阻止對 cookie 中敏感信息的所有訪問。例如,有權訪問客户端硬盤(或 JavaScript,如果HttpOnly未設置該屬性)的人可以讀取和修改信息。
JavaScript API HttpOnly:無法訪問具有該屬性的 cookie ;
Document.cookie它只發送到服務器。
例如,持續存在於服務器端會話中的 cookie 不需要對 JavaScript 可用,並且應該具有該HttpOnly屬性。這種預防措施有助於緩解跨站點腳本 ( XSS ) 攻擊。
這是一個例子:
Set-Cookie: id=a3fWa; Expires=Thu, 21 Oct 2021 07:28:00 GMT; Secure; HttpOnly
4、定義 cookie 的發送位置
Domain和Path屬性定義了 cookie的範圍:cookie 應該發送到哪些 URL。
4.1 Domain屬性
Domain屬性指定哪些主機可以接收 cookie。如果未指定,則該屬性默認為設置 cookie 的同一主機,不包括 subdomains。
如果指定Domain 則始終包含子域。因此,指定Domain比省略它的限制要小。但是,當子域需要共享有關用户的信息時,它會很有幫助。
例如,如果您設置Domain=mozilla.org,則 cookie 可用於子域,如developer.mozilla.org.
4.2 Path屬性
Path屬性指示在請求的 URL 中必須存在的 URL 路徑,以便發送Cookie標頭。( %x2F"/") 字符被視為目錄分隔符,子目錄也匹配。
例如,如果您設置Path=/docs,則這些請求路徑匹配:
/docs
/docs/
/docs/Web/
/docs/Web/HTTP
但是這些請求路徑不會:
/
/docsets
/fr/docs
4.3 SameSite屬性
SameSite屬性允許服務器指定是否何時通過跨站點請求發送 cookie(其中站點由可註冊域和方案定義:http 或 https)。這提供了一些針對跨站點請求偽造攻擊 ( CSRF ) 的保護。它需要三個可能的值:Strict、Lax和None。
使用Strict時,cookie 只會發送到它的來源站點。
Lax類似,除了當用户導航到 cookie 的源站點時發送 cookie。例如,通過跟蹤來自外部站點的鏈接。
None指定在發起請求和跨站點請求時都發送 cookie,但僅在安全上下文中(即,如果還必須設置SameSite=None該屬性)。
注意:如果未設置SameSite屬性,則 cookie 默認被視為Lax.
例子:如:網站a.com有cookie:name=Justin,samesite=xxx,網站b.com無cookie
- 1、當xxx=strict,b.com訪問a.com的接口時,get和post請求都不會把a.com的cookie帶到a的接口
- 2、當xxx=lax,b.com訪問a.com的接口時,get會把a.com的cookie帶到a的接口,post不會
-
3、當xxx=none,b.com訪問a.com的接口時,get和post請求都會把a.com的cookie帶到a的接口
Set-Cookie: mykey=myvalue; SameSite=Strict|Lax|None
注意:如果b.com是a.com的子域,如:b.com=api.a.com的話,不管samesite等於啥,都會把a.com的cookie帶過去的
問題
介紹完Cookie之後,我們假設我們現在有個前後台分離的項目,前端地址是:www.baidu.com,後台api地址是:api.baidu.com和api.bilibili.com,現在我們來分析講下面幾個問題:
1、跨域前後台設置問題
2、跨域Cookie不能攜帶問題
3、Cookie如何如何避免XSS攻擊和CSRF攻擊
1、跨域前後台設置問題
如果想讓前端訪問後台兩個api地址我們需要設置如下代碼,
前端(以axios為例):
axios.defaults.withCredentials = true
後台(以spring cloud gateway為例)
spring:
cloud:
loadbalancer.ribbon.enabled: false
gateway:
globalcors:
#add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
allowedHeaders: "*"
allowCredentials: true
allowedOrigins:
- "https://api.baidu.com"
- "https://api.bilibili.com"
allowedMethods: "*"
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials
2、跨域Cookie不能攜帶問題
這裏就有意思了,因為我們前端的地址是www.baidu.com,簡稱A,後台api有兩個地址:api.baidu.com,簡稱B和api.bilibili.com,簡稱C,當我們通過A請求B的接口的時候,cookie會自動帶上,因為他們是屬於同源不同域,所以你在A下設置的cookie在請求B時也會帶上,而當A請求C時就帶不了了,明顯兩個不同源也不同域,所以無法帶上,如果真要帶的話,只有當A請求C時會把C的cookie帶給C,而不能把A的cookie帶給C,所以現在你知道該怎麼做了吧!
3、Cookie如何避免XSS攻擊和CSRF攻擊
XSS介紹:
(Cross Site Script )攻擊全稱為跨站腳本攻擊,xss攻擊通常指的是利用網頁開發留下的漏洞,通過巧妙的方法注入惡意的指令代碼到網頁上,使用户加載並執行惡意製造的網頁程序。這些惡意網頁程序通常是JavaScript代碼
XSS解決:
使用Cookie的HttpOnly屬性
嚴格來説,HttpOnly並非是為了對抗XSS-HttpOnly解決的是XSS後的Cookie劫持攻擊。前面説了XSS可能會竊取用户的Cookie,然後就直接登錄了該用户的賬户,但是如果Cookie設置了HttpOnly,則這種攻擊會失敗,因為JavaScript讀取不到Cookie的值。
CSRF介紹:
CSRF攻擊的全稱是跨站請求偽造( cross site request forgery),是一種對網站的惡意利用。簡單點講就是,惡意網站(攻擊者)盜用了你的身份,以你的名義向信任網站發送惡意請求。 CRSF能做的事情包括利用你的身份發郵件、發短信、進行交易轉賬等,甚至盜取你的賬號。
CSRF 攻擊的三個必要條件:
目標站點一定要有 CSRF 漏洞
用户要登錄過目標站點,並且在瀏覽器上保持有該站點的登錄狀態
需要用户打開一個第三方站點,可以是黑客的站點,也可以是一些論壇
CSRF 攻擊不會往頁面注入惡意腳本,而是找服務器的漏洞,因此黑客是無法通過 CSRF 攻擊來獲取用户頁面數據的,對於 CSRF 攻擊,主要的防護手段是提升服務器的安全性。
CSRF解決:
如果是從第三方站點發起的請求,那麼需要瀏覽器禁止發送某些關鍵 Cookie 數據到服務器;
如果是同一個站點發起的請求,那麼就需要保證 Cookie 數據正常發送。
而 Cookie 中的 SameSite 屬性正是為了解決這個問題的。
在 HTTP 響應頭中,通過 set-cookie 字段設置 Cookie 時,可以帶上 SameSite 選項。
總結
1、cookie是所有前端開發人員必須要面對的知識點,大家一定要把原理弄清楚,這樣在實際開發中遇到跨域還是xss、csrf攻擊防範的話都能夠有的放矢
引用
使用 HTTP cookie
瀏覽器原理 33 # CSRF攻擊:為什麼Cookie中有SameSite屬性?
【信息安全】面試常問CSRF、cookie、session和token
瀏覽器跨域請求成功了,但是cookie 沒有帶過去,求解?