一、Cookie
Cookie嚴格來說是存儲空間 + 通信機制,是個載體,用提交持久化的信息。
瀏覽器將一條條數據存儲在一個叫Cookies的空間中,這些數據的結構包含:鍵、值、屬性
Cookie的通信機制為:瀏覽器中某個網站(域)發送HTTP請求時,瀏覽器會自動將此域的所有Cookie放在所有HTTP報文的Cookie Header中,抓包能發現
二、Session
Session是身份認證的憑證,用戶登錄后,服務器分發一個憑證,以后用戶再請求帶上這個憑證就能明確身份,不用每次都用賬號密碼來證明身份
這么重要的數據當然得保存在服務端,且以服務端的為准。但客戶端也得保存,才能在登錄周期中每次請求都帶上
"每次請求都帶上"這顯然就和Cookie一樣,所以就有很多開發者把Session放在Cookies里,不用自己寫前端代碼把Session塞進請求里,瀏覽器自己會帶上
而瀏覽器開發者也覺得這樣很合理,也為Cookie添加了一些可設置的安全相關屬性
三、Cookie和Session的區別
百度一下最常見的就是"Cookie保存在客戶端而Session保存在服務端",很多人看了有疑惑,明明Session就在Cookie中啊,為什么這么說?二者到底有啥區別?
但當我們說Cookie和Session的區別時,這里的Cookie就理應是一種和Session一樣的認證機制,這種情況下Session借助Cookies暫存在瀏覽器中,二者都在瀏覽器和服務器中被存儲,這是Cookies最重要的應用
那如何解釋"Cookie保存在客戶端"呢?可能本來就是瞎說,也可能是最開始有這種場景:
網站設計時,服務端可能每次收到請求都需要知道客戶端的某種信息,比如用戶登錄時間,這種信息又不足以重要到需要保存在服務端,那么可以借助Cookie
數據在前端生成,或在服務端生成,生成后通過Set-Cookie發送給客戶端后,服務端自身不保存,當客戶端再次有請求時,服務端可以從請求的HTTP Header中調出這個數據
以及傳聞中cookie名字的由來:
設想你某次登陸過一個網站,下次登錄的時候不想再次輸入賬號了,怎么辦?這個信息可以寫到Cookie里面,訪問網站的時候,網站頁面的腳本可以讀取這個信息,就自動幫你把用戶名給填了,能夠方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。
我比較認可的解釋是Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集群、數據庫、文件中;Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式[1]。
四、token
其實就字面意思來說,token(令牌),應該是用戶憑證,服務器校驗它來判斷當前請求是否來自於登錄用戶。
而Session(會話),應該是一次連續的、短暫的交流。在交流過程中,服務器可以通過Session識別這次會話,區別於其它正在進行的會話。這里的會話可以是一次支付業務,比如用戶創建了訂單,帶着一個session跳到支付平台,回來服務器再校驗是否支付成功。這種會話有明確的起點、終點,且不能被中斷,比如你關閉瀏覽器,再打開頁面,你仍是登錄狀態,但是支付流程卻只能重新走。這種特性讓session在瀏覽器上更適合存放在Session Storage中
然而現實和字面意思卻相反,開發者們習慣用Session來做令牌,用token來做會話,其它用於校驗身份的參數也習慣命名為token
五、token和Session的區別
Session機制通過Cookie來實現。只需在登錄成功后,響應中有Set-Cookie Header來設置一個Cookie值,瀏覽器就會自動保存、刷新Cookie值,后續請求會自動攜帶此Cookie值,完全不用前端代碼
而token機制一般不通過Cookie來實現,它一般通過自定義一個HTTP Header來實現,或放在HTTP參數中來實現,需要前端手動將其塞進報文中
即使token為了長期存儲放在Cookie中,后端最好也不要通過Cookie Header來校驗,比如csrf-token,就得通過HTTP Header、HTTP參數來校驗才有意義
六、三者涉及的用戶認證安全問題
目前的瀏覽器有以下空間供網站使用
-
Cookies:持久保存,大小限制4kb,最多放20個,每次請求都會帶上所有Cookie。包含特殊功能屬性
-
Session Storage:關閉標簽后失效,請求不會主動帶上
-
Local Storage:持久保存,請求不會主動帶上
我們再來看幾個Web的安全漏洞
-
XSS,跨站腳本攻擊。攻擊者獲得頁面的js權限,能操作頁面的一切,包括Session Storage和未設置HTTP Only屬性的Cookie
-
CSRF,跨站請求偽造,就是釣魚網站,在網站A,發送一個向網站B的請求,由於瀏覽器會在請求中加上此域的Cookie,所以如果在瀏覽器登錄了網站B,又打開了釣魚網站A,網站A構造的前往網站B的請求就會帶上你的Cookie,如果Cookie中有用戶憑證,服務器認為你是登錄用戶,請求就會被響應。比如點贊、關注、發色情廣告等請求
防御XSS,把關機Cookie設置HTTP Only屬性即可,js無權訪問
而防御CSRF,推薦使用token機制,服務器頒發token,按規范來說推薦存儲在Session Storage中(也可以放cookie中,由於瀏覽器的同源策略,網站A無法訪問網站B的cookie),js將其添加到請求的參數或Header中
以上兩個措施,都需要做到,才能防護這兩個攻擊。
另外博主以為,現在服務器校驗referer Header也是能防御CSRF的
四、錯誤示范
-
把用戶登錄憑證放在Cookie中,未設置HTTP Only,無token機制
容易遭受XSS、CSRF攻擊
-
把用戶登錄憑證放在Cookie中,設置了HTTP Only,無token機制
容易遭受CSRF攻擊,成功防御XSS攻擊
-
把用戶登錄憑證放在Cookie中,未設置HTTP Only,無token機制(前端把token保存在Session Storage。或保存在Cookies且未設置HTTP Only,后端通過HTTP Header、HTTP參數來校驗)
容易遭受XSS攻擊,攻擊者能構造請求,把token拼上,成功防御CSRF攻擊
-
把用戶登錄憑證放在Cookie中,設置了HTTP Only,請求中有token參數/Header【規范防御】
成功防御XSS、CSRF攻擊
-
把用戶登錄憑證放在Cookie中,未設置HTTP Only,請求中有token參數/Header(前端把token保存在保存在Cookies,設置了HTTP Only)【稀里糊塗地防御】
成功防御XSS、CSRF攻擊。但不可能,Cookie都未設置HTTP Only,token為啥會這么巧設置了
-
把用戶登錄憑證放在Cookie中,設置了HTTP Only,且請求參數/Header中也添加並校驗此憑證【想屁吃】
當你以為這樣后端用一串隨機字符串就能解決校驗的時候,覺得發現一個省事竅門的時候,瀏覽器會告訴你,設置了HTTP Only的Cookie,js無法拿到,無法放到請求參數/Header中
-
把用戶登錄憑證放在Cookie中,設置了HTTP Only,且服務器校驗referer Header【極簡防御二】
一個憑證同時防御XSS、CSRF攻擊,瀏覽器js目前無法修改請求的referer頭,會提示
Refused to set unsafe header "Referer"
五、最后
現在瀏覽器的安全也逐步做上來了,一定程度上能防御CSRF,除了cookie samesite,跨域在POST請求前,還會發送OPTIONS請求,如果OPTIONS請求不通過,無法POST
但開發還是要規范,不能指望用戶都是新瀏覽器
以上,講得不對望指正