本文內(nèi)容大多參考《圖解HTTP》一書
1. 使用Cookie來管理狀態(tài)
HTTP 是無狀態(tài)協(xié)議所森,說明它不能以狀態(tài)來區(qū)分和管理請求和響應(yīng)焕济。也就是說盔几,無法根據(jù)之前的狀態(tài)進(jìn)行本次的請求處理。
不可否認(rèn)上鞠,無狀態(tài)協(xié)議當(dāng)然也有它的優(yōu)點(diǎn)芯丧。由于不必保存狀態(tài),自然可減少服務(wù)器的CPU 及內(nèi)存資源的消耗谴咸。從另一側(cè)面來說,也正是因?yàn)镠TTP 協(xié)議本身是非常簡單的骗露,所以才會被應(yīng)用在各種場景里岭佳。
我們登錄淘寶的時(shí)候首先要登錄,我們看到了一個(gè)商品點(diǎn)進(jìn)去萧锉,進(jìn)行了頁面跳轉(zhuǎn)/刷新珊随,按照HTTP的無狀態(tài)協(xié)議豈不是又要登錄一次?
所以為了解決這個(gè)問題柿隙,Cookie誕生了叶洞,在保留無狀態(tài)協(xié)議這個(gè)特征的同時(shí)又要解決類似記錄狀態(tài)的矛盾問題。Cookie 技術(shù)通過在請求和響應(yīng)報(bào)文中寫入Cookie 信息來控制客戶端的狀態(tài)京办。
Cookie 會根據(jù)從服務(wù)器端發(fā)送的響應(yīng)報(bào)文內(nèi)的一個(gè)叫做Set-Cookie的首部字段信息,通知客戶端保存Cookie帆焕。當(dāng)下次客戶端再往該服務(wù)器發(fā)送請求時(shí),客戶端會自動在請求報(bào)文中加入Cookie 值后發(fā)送出去不恭。
服務(wù)器端發(fā)現(xiàn)客戶端發(fā)送過來的Cookie 后叶雹,會去檢查究竟是從哪一個(gè)客戶端發(fā)來的連接請求,然后對比服務(wù)器上的記錄换吧,最后得到之前的狀態(tài)信息折晦。
- 沒有Cookie信息狀態(tài)下的請求
- 第2次以后(存有Cookie信息狀態(tài))的請求
上圖很清晰地展示了發(fā)生Cookie 交互的情景。
HTTP 請求報(bào)文和響應(yīng)報(bào)文的內(nèi)容如下(數(shù)字和圖中對應(yīng))沾瓦。
①請求報(bào)文(沒有Cookie 信息的狀態(tài))
GET /reader/ HTTP/1.1
Host: hackr.jp
*首部字段內(nèi)沒有Cookie的相關(guān)信息
②響應(yīng)報(bào)文(服務(wù)器端生成Cookie 信息)
HTTP/1.1 200 OK
Date: Thu, 12 Jul 2012 07:12:20 GMT
Server: Apache
<Set-Cookie: sid=1342077140226724; path=/; expires=Wed,10-Oct-12 07:12:20 GMT>
Content-Type: text/plain; charset=UTF-8
③請求報(bào)文(自動發(fā)送保存著的Cookie 信息)
GET /image/ HTTP/1.1
Host: hackr.jp
Cookie: sid=1342077140226724
2. 關(guān)于Cookie 的首部字段
首部字段名 | 說明 | 首部類型 |
---|---|---|
Set-Cookie | 開始狀態(tài)管理所使用的Cookie信息 | 響應(yīng)首部字段 |
Cookie | 服務(wù)器接收到的Cookie信息 | 請求首部字段 |
2.1 Set-Cookie
Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 GMT; ?
path=/; domain=.hackr.jp;
當(dāng)服務(wù)器準(zhǔn)備開始管理客戶端的狀態(tài)時(shí)满着,會事先告知各種信息谦炒。下面的表格列舉了Set-Cookie 的字段值。
2.1.1Set-Cookie 字段的屬性
屬性 | 說明 |
---|---|
NAME=VALUE | 賦予Cookie的名稱和其值(必需項(xiàng)) |
expires=DATE | Cookie的有效期(若不明確指定則默認(rèn)為瀏覽器關(guān)閉前為止) |
path=PATH | 將服務(wù)器上的文件目錄作為Cookie的適用對象(若不指定則默認(rèn)為文檔所在的文件目錄) |
domain=域名 | 作為Cookie適用對象的域名 (若不指定則默認(rèn)為創(chuàng)建Cookie的服務(wù)器的域名) |
2.1.2 expires 屬性
Cookie 的expires 屬性指定瀏覽器可發(fā)送Cookie 的有效期风喇。當(dāng)省略expires 屬性時(shí)宁改,Cookie僅在瀏覽器關(guān)閉之前有效。
另外魂莫,一旦Cookie 從服務(wù)器端發(fā)送至客戶端还蹲,服務(wù)器端就不存在可以顯式刪除Cookie 的方法。但可通過覆蓋已過期的Cookie耙考,實(shí)現(xiàn)對客戶端Cookie 的實(shí)質(zhì)性刪除操作谜喊。
2.1.3 path 屬性
Cookie 的path 屬性可用于限制指定Cookie 的發(fā)送范圍的文件目錄。不過另有辦法可避開這項(xiàng)限制倦始,看來對其作為安全機(jī)制的效果不能抱有期待斗遏。
2.1.4 domain 屬性
通過Cookie 的domain 屬性指定的域名可做到與結(jié)尾匹配一致。比如鞋邑, 當(dāng)指定example.com 后诵次, 除example.com 以外,www.example.com或www2.example.com 等都可以發(fā)送Cookie炫狱。因此藻懒,除了針對具體指定的多個(gè)域名發(fā)送Cookie 之外,不指定domain 屬性顯得更安全视译。
2.1.5 secure 屬性
Cookie 的secure 屬性用于限制Web 頁面僅在HTTPS 安全連接時(shí)嬉荆,才可以發(fā)送Cookie。發(fā)送Cookie 時(shí)酷含,指定secure 屬性的方法如下所示鄙早。
Set-Cookie: name=value; secure
以上例子僅當(dāng)在https ://www.example.com/(HTTPS)安全連接的情況下才會進(jìn)行Cookie 的回收。也就是說椅亚,即使域名相同時(shí)http : //www.example.com/(HTTP) 也不會發(fā)生Cookie 回收行為限番。當(dāng)省略secure 屬性時(shí),不論HTTP 還是HTTPS呀舔,都會對Cookie 進(jìn)行回收弥虐。
2.1.6 HttpOnly 屬性
Cookie 的HttpOnly 屬性是Cookie 的擴(kuò)展功能,它使JavaScript 腳本無法獲得Cookie媚赖。其主要目的為防止跨站腳本攻擊(Cross-sitescripting霜瘪,XSS)對Cookie 的信息竊取。
發(fā)送指定HttpOnly 屬性的Cookie 的方法如下所示惧磺。
Set-Cookie: name=value; HttpOnly
通過上述設(shè)置颖对,通常從Web 頁面內(nèi)還可以對Cookie 進(jìn)行讀取操作。但使用JavaScript 的document.cookie 就無法讀取附加HttpOnly 屬性后的Cookie 的內(nèi)容了磨隘。因此缤底,也就無法在XSS 中利用JavaScript 劫持Cookie 了顾患。
雖然是獨(dú)立的擴(kuò)展功能,但I(xiàn)nternet Explorer 6 SP1 以上版本等當(dāng)下的主流瀏覽器都已經(jīng)支持該擴(kuò)展了个唧。另外順帶一提江解,該擴(kuò)展并非是為了防止XSS 而開發(fā)的。
2.2 Cookie
Cookie: status=enable
首部字段Cookie 會告知服務(wù)器坑鱼,當(dāng)客戶端想獲得HTTP 狀態(tài)管理支持時(shí)膘流,就會在請求中包含從服務(wù)器接收到的Cookie。接收到多個(gè)Cookie 時(shí)鲁沥,同樣可以以多個(gè)Cookie 形式發(fā)送呼股。
3 Session 管理及Cookie 應(yīng)用
3.1 什么是Session
在計(jì)算機(jī)中,尤其是在網(wǎng)絡(luò)應(yīng)用中画恰,稱為“會話控制”彭谁。Session 對象存儲特定用戶會話所需的屬性及配置信息。這樣允扇,當(dāng)用戶在應(yīng)用程序的 Web 頁之間跳轉(zhuǎn)時(shí)缠局,存儲在 Session 對象中的變量將不會丟失,而是在整個(gè)用戶會話中一直存在下去考润。當(dāng)用戶請求來自應(yīng)用程序的 Web 頁時(shí)狭园,如果該用戶還沒有會話,則 Web 服務(wù)器將自動創(chuàng)建一個(gè) Session 對象糊治。當(dāng)會話過期或被放棄后唱矛,服務(wù)器將終止該會話。Session 對象最常見的一個(gè)用法就是存儲用戶的首選項(xiàng)井辜。例如绎谦,如果用戶指明不喜歡查看圖形,就可以將該信息存儲在 Session 對象中粥脚。
3.2 通過Cookie來管理Session
基于表單認(rèn)證的標(biāo)準(zhǔn)規(guī)范尚未有定論窃肠,一般會使用Cookie 來管理Session(會話)。
基于表單認(rèn)證本身是通過服務(wù)器端的Web 應(yīng)用刷允,將客戶端發(fā)送過來的用戶ID 和密碼與之前登錄過的信息做匹配來進(jìn)行認(rèn)證的冤留。
但鑒于HTTP 是無狀態(tài)協(xié)議,之前已認(rèn)證成功的用戶狀態(tài)無法通過協(xié)議層面保存下來树灶。即搀菩,無法實(shí)現(xiàn)狀態(tài)管理,因此即使當(dāng)該用戶下一次繼續(xù)訪問破托,也無法區(qū)分他與其他的用戶。于是我們會使用Cookie 來管理Session歧蒋,以彌補(bǔ)HTTP 協(xié)議中不存在的狀態(tài)管理功能土砂。
- 步驟一:客戶端把用戶ID 和密碼等登錄信息放入報(bào)文的實(shí)體部分州既,通常是以POST 方法把請求發(fā)送給服務(wù)器。而這時(shí)萝映,會使用HTTPS 通信來進(jìn)行HTML 表單畫面的顯示和用戶輸入數(shù)據(jù)的發(fā)送吴叶。
-
步驟二:服務(wù)器會發(fā)放用以識別用戶的Session ID。通過驗(yàn)證從客戶端發(fā)送過來的登錄信息進(jìn)行身份認(rèn)證序臂,然后把用戶的認(rèn)證狀態(tài)與Session ID 綁定后記錄在服務(wù)器端蚌卤。
向客戶端返回響應(yīng)時(shí),會在首部字段Set-Cookie 內(nèi)寫入Session ID(如PHPSESSID=028a8c…)奥秆。
你可以把Session ID 想象成一種用以區(qū)分不同用戶的等位號逊彭。然而,如果Session ID 被第三方盜走构订,對方就可以偽裝成你的身份進(jìn)行惡意操作了侮叮。因此必須防止Session ID 被盜,或被猜出悼瘾。為了做到這點(diǎn)囊榜,Session ID 應(yīng)使用難以推測的字符串,且服務(wù)器端也需要進(jìn)行有效期的管理亥宿,保證其安全性卸勺。
另外,為減輕跨站腳本攻擊(XSS)造成的損失烫扼,建議事先在Cookie 內(nèi)加上httponly 屬性曙求。 - 步驟三:客戶端接收到從服務(wù)器端發(fā)來的Session ID 后,會將其作為Cookie 保存在本地材蛛。下次向服務(wù)器發(fā)送請求時(shí)圆到,瀏覽器會自動發(fā)送Cookie,所以Session ID 也隨之發(fā)送到服務(wù)器卑吭。服務(wù)器端可通過驗(yàn)證接收到的Session ID 識別用戶和其認(rèn)證狀態(tài)芽淡。
除了以上介紹的應(yīng)用實(shí)例,還有應(yīng)用其他不同方法的案例豆赏。
另外挣菲,不僅基于表單認(rèn)證的登錄信息及認(rèn)證過程都無標(biāo)準(zhǔn)化的方法,服務(wù)器端應(yīng)如何保存用戶提交的密碼等登錄信息等也沒有標(biāo)準(zhǔn)化掷邦。
通常白胀,一種安全的保存方法是,先利用給密碼加鹽(salt)A 的方式增加額外信息抚岗,再使用散列(hash)函數(shù)計(jì)算出散列值后保存或杠。但是我們也經(jīng)常看到直接保存明文密碼的做法宣蔚,而這樣的做法具有導(dǎo)致密碼泄露的風(fēng)險(xiǎn)向抢。