首先,HTTP協(xié)議是無狀態(tài)(stateless)的穴店。Cookie和Session都是在無狀態(tài)的HTTP協(xié)議上來維護會話狀態(tài)阵谚。
因為HTTP協(xié)議是無狀態(tài)的衔憨,每次用戶請求到達服務器時,HTTP服務器不知道用戶是誰宴抚,是否登錄過勒魔,服務器之所以知道我們登錄過,是因為服務器在我們登錄時設置了瀏覽器的Cookie。Session則是借由Cookie實現(xiàn)更高層的服務器與瀏覽器間的會話酱塔。
Cookie實現(xiàn)機制
Cookie 是客戶端保存的小型文本文件沥邻,內容為一系列鍵值對。
Cookie是HTTP服務器設置的羊娃,并且保存在瀏覽器中唐全。
用戶再訪問其他頁面時,會在HTTP請求中保存之前的Cookie蕊玷。
Cookie的具體傳遞流程:
- 瀏覽器向URL發(fā)起HTTP請求邮利,這個請求可以是GET一個頁面,POST一個登錄表單等垃帅。
- 對應的服務器接受到HTTP請求延届,并計算應當返回給瀏覽器的HTTP響應,HTTP響應包括響應頭和響應體兩個部分贸诚。
- 響應頭中加入
Set-Cookie
字段方庭,他的值是要設置的Cookie厕吉。
在 RFC2109 6.3 Implementation Limits 中提到: UserAgent(瀏覽器就是一種用戶代理)至少應支持300項Cookie, 每項至少應支持到4096字節(jié)械念,每個域名至少支持20項Cookie头朱。 - 瀏覽器接收到服務器發(fā)來的HTTP響應。
- 瀏覽器收到HTTP響應龄减,發(fā)現(xiàn)Set-Cookie字段项钮,并將字段的值保存在硬盤或者內存中。
Set-Cookie字段的值可以是很多項Cookie希停,每一項都可以指定過期時間Expires烁巫。 - 瀏覽器下次給服務器發(fā)送HTTP請求時,會將服務器設置的Cookie附加在HTTP請求的頭字段Cookie中宠能。
瀏覽器可以存儲多個域名下的Cookie亚隙,但是只發(fā)送當前請求的域名曾指定過的Cookie。 - 服務器接收到HTTP請求棍潘,發(fā)現(xiàn)請求頭中有Cookie字段恃鞋,就知道之前已和這個用戶打過招呼了。
總之亦歉,服務器通過Set-Cookie響應頭字段來指示瀏覽器保存Cookie恤浪, 瀏覽器通過Cookie請求頭字段來告訴服務器之前的狀態(tài)。 Cookie中包含若干個鍵值對肴楷,每個鍵值對可以設置過期時間水由。
Cookie的隱患
Cookie是有隱患的。
第一次服務器驗證用戶和密碼赛蔫,設置Set-Cookie為authed=true砂客,后返回 200(OK)。
于是瀏覽器再次發(fā)帶有Cookie信息的HTTP請求(設置authed=true)呵恢,服務器會得知用戶已登錄鞠值,于是按照登錄的權限去處理這次請求。
但是渗钉,假如用HTTP客戶端軟件(Node.js)來模擬瀏覽器發(fā)請求彤恶,明文設置相同的Cookie字段,并設置authed=true鳄橘,服務器會不會被騙声离?這種攻擊十分容易,因為服務器是會被篡改的瘫怜。
Cookie防篡改
服務器可以為每個Cookie項生成簽名术徊,由于用戶篡改Cookie后無法生成對應的簽名, 服務器便可以得知用戶對Cookie進行了篡改鲸湃。
- 在服務器中配置一個不為人知的字符串(我們叫它Secret)赠涮,比如:x$sfz32子寓。
- 當服務器需要設置Cookie時(比如authed=false),不僅設置authed的值為false世囊, 在值的后面進一步設置一個簽名别瞭,最終設置的Cookie是authed=false|6hTiBl7lVpd1P。
- 簽名6hTiBl7lVpd1P是這樣生成的:Hash('x$sfz32'+'false')株憾。 要設置的值與Secret相加再取哈希。
- 用戶收到HTTP響應并發(fā)現(xiàn)頭字段Set-Cookie: authed=false|6hTiBl7lVpd1P晒衩。
- 用戶在發(fā)送HTTP請求時嗤瞎,篡改了authed值,設置頭字段Cookie: authed=true|???听系。 因為用戶不知道Secret贝奇,無法生成簽名,只能隨便填一個靠胜。
- 服務器收到HTTP請求掉瞳,發(fā)現(xiàn)Cookie: authed=true|???。服務器開始進行校驗: Hash('true'+'x$sfz32')浪漠,便會發(fā)現(xiàn)用戶提供的簽名不正確陕习。
通過給Cookie添加簽名,使得服務器得以知道Cookie被篡改址愿。
但是该镣,Cookie是明文傳輸的, 只要服務器設置過一次authed=true|xxxx就知道true的簽名是xxxx了响谓, 以后就可以用這個簽名來欺騙服務器了损合。因此Cookie中最好不要放敏感數據。 一般來講Cookie中只會放一個Session Id娘纷,而Session存儲在服務器端嫁审。
Session實現(xiàn)機制
Session一般用來存儲重要數據,一般儲存在HTTP服務器內存中赖晶,或者內存數據庫中(redis)律适,對于重量級應用甚至可以儲存在數據庫中。
下面以儲存在redis中為例嬉探,考察如何驗證用戶登陸狀態(tài)的問題擦耀。
- 用戶提交賬戶和密碼的表單,發(fā)送POST請求涩堤。
- 服務器驗證賬戶和密碼眷蜓,若正確則把當前用戶名儲存到redis中,在redis中生成對應的id胎围。
- 服務器設置Cookie為SessionID并發(fā)送HTTP響應吁系,也同樣設置簽名德召。
- 用戶收到HTTP響應后,便看不到敏感數據了汽纤,然后發(fā)送Session Cookie字段的請求給服務器上岗。
- 服務器收到請求后,同樣進行Session Cookie的防篡改驗證蕴坪,若通過驗證肴掷,則根據ID從redis中取出用戶對象,查看該對象的狀態(tài)并繼續(xù)執(zhí)行業(yè)務邏輯背传。