認識HTTP----Cookie和Session篇

本文內容大多參考《圖解HTTP》一書

1. 使用Cookie來管理狀態

HTTP 是無狀態協議,說明它不能以狀態來區分和管理請求和響應。也就是說,無法根據之前的狀態進行本次的請求處理。

不可否認,無狀態協議當然也有它的優點。由於不必保存狀態,自然可減少伺服器的CPU 及內存資源的消耗。從另一側面來說,也正是因為HTTP 協議本身是非常簡單的,所以才會被應用在各種場景里。

我們登錄淘寶的時候首先要登錄,我們看到了一個商品點進去,進行了頁面跳轉/刷新,按照HTTP的無狀態協議豈不是又要登錄一次?

所以為了解決這個問題,Cookie誕生了,在保留無狀態協議這個特徵的同時又要解決類似記錄狀態的矛盾問題。Cookie 技術通過在請求和響應報文中寫入Cookie 信息來控制客戶端的狀態。

Cookie 會根據從伺服器端發送的響應報文內的一個叫做Set-Cookie的首部欄位信息,通知客戶端保存Cookie。當下次客戶端再往該伺服器發送請求時,客戶端會自動在請求報文中加入Cookie 值後發送出去。

伺服器端發現客戶端發送過來的Cookie 後,會去檢查究竟是從哪一個客戶端發來的連接請求,然後對比伺服器上的記錄,最後得到之前的狀態信息。

  • 沒有Cookie信息狀態下的請求

  • 第2次以後(存有Cookie信息狀態)的請求

上圖很清晰地展示了發生Cookie 交互的情景。

HTTP 請求報文和響應報文的內容如下(數字和圖中對應)。

①請求報文(沒有Cookie 信息的狀態)

GET /reader/ HTTP/1.1nHost: hackr.jpn*首部欄位內沒有Cookie的相關信息n

②響應報文(伺服器端生成Cookie 信息)

HTTP/1.1 200 OKnDate: Thu, 12 Jul 2012 07:12:20 GMTnServer: Apachen<Set-Cookie: sid=1342077140226724; path=/; expires=Wed,10-Oct-12 07:12:20 GMT>nContent-Type: text/plain; charset=UTF-8n

③請求報文(自動發送保存著的Cookie 信息)

GET /image/ HTTP/1.1nHost: hackr.jpnCookie: sid=1342077140226724n

2. 關於Cookie 的首部欄位

2.1 Set-Cookie

Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 GMT; ?npath=/; domain=.hackr.jp;n

當伺服器準備開始管理客戶端的狀態時,會事先告知各種信息。下面的表格列舉了Set-Cookie 的欄位值。

2.1.1Set-Cookie 欄位的屬性

2.1.2 expires 屬性

Cookie 的expires 屬性指定瀏覽器可發送Cookie 的有效期。當省略expires 屬性時,Cookie僅在瀏覽器關閉之前有效。

另外,一旦Cookie 從伺服器端發送至客戶端,伺服器端就不存在可以顯式刪除Cookie 的方法。但可通過覆蓋已過期的Cookie,實現對客戶端Cookie 的實質性刪除操作。

2.1.3 path 屬性

Cookie 的path 屬性可用於限制指定Cookie 的發送範圍的文件目錄。不過另有辦法可避開這項限制,看來對其作為安全機制的效果不能抱有期待。

2.1.4 domain 屬性

通過Cookie 的domain 屬性指定的域名可做到與結尾匹配一致。比如, 當指定http://example.com 後, 除http://example.com 以外,Example Domain或www2.example.com 等都可以發送Cookie。因此,除了針對具體指定的多個域名發送Cookie 之外,不指定domain 屬性顯得更安全。

2.1.5 secure 屬性

Cookie 的secure 屬性用於限制Web 頁面僅在HTTPS 安全連接時,才可以發送Cookie。發送Cookie 時,指定secure 屬性的方法如下所示。

Set-Cookie: name=value; secure

以上例子僅當在https ://Example Domain(HTTPS)安全連接的情況下才會進行Cookie 的回收。也就是說,即使域名相同時http : //Example Domain(HTTP) 也不會發生Cookie 回收行為。當省略secure 屬性時,不論HTTP 還是HTTPS,都會對Cookie 進行回收。

2.1.6 HttpOnly 屬性

Cookie 的HttpOnly 屬性是Cookie 的擴展功能,它使JavaScript 腳本無法獲得Cookie。其主要目的為防止跨站腳本攻擊(Cross-sitescripting,XSS)對Cookie 的信息竊取。

發送指定HttpOnly 屬性的Cookie 的方法如下所示。

Set-Cookie: name=value; HttpOnly

通過上述設置,通常從Web 頁面內還可以對Cookie 進行讀取操作。但使用JavaScript 的document.cookie 就無法讀取附加HttpOnly 屬性後的Cookie 的內容了。因此,也就無法在XSS 中利用JavaScript 劫持Cookie 了。

雖然是獨立的擴展功能,但Internet Explorer 6 SP1 以上版本等當下的主流瀏覽器都已經支持該擴展了。另外順帶一提,該擴展並非是為了防止XSS 而開發的。

2.2 Cookie

Cookie: status=enable

首部欄位Cookie 會告知伺服器,當客戶端想獲得HTTP 狀態管理支持時,就會在請求中包含從伺服器接收到的Cookie。接收到多個Cookie 時,同樣可以以多個Cookie 形式發送。

3 Session 管理及Cookie 應用

3.1 什麼是Session

在計算機中,尤其是在網路應用中,稱為「會話控制」。Session 對象存儲特定用戶會話所需的屬性及配置信息。這樣,當用戶在應用程序的 Web 頁之間跳轉時,存儲在 Session 對象中的變數將不會丟失,而是在整個用戶會話中一直存在下去。當用戶請求來自應用程序的 Web 頁時,如果該用戶還沒有會話,則 Web 伺服器將自動創建一個 Session 對象。當會話過期或被放棄後,伺服器將終止該會話。Session 對象最常見的一個用法就是存儲用戶的首選項。例如,如果用戶指明不喜歡查看圖形,就可以將該信息存儲在 Session 對象中。

3.2 通過Cookie來管理Session

基於表單認證的標準規範尚未有定論,一般會使用Cookie 來管理Session(會話)。

基於表單認證本身是通過伺服器端的Web 應用,將客戶端發送過來的用戶ID 和密碼與之前登錄過的信息做匹配來進行認證的。

但鑒於HTTP 是無狀態協議,之前已認證成功的用戶狀態無法通過協議層面保存下來。即,無法實現狀態管理,因此即使當該用戶下一次繼續訪問,也無法區分他與其他的用戶。於是我們會使用Cookie 來管理Session,以彌補HTTP 協議中不存在的狀態管理功能。

Session 管理及Cookie 狀態管理

  • 步驟一:客戶端把用戶ID 和密碼等登錄信息放入報文的實體部分,通常是以POST 方法把請求發送給伺服器。而這時,會使用HTTPS 通信來進行HTML 表單畫面的顯示和用戶輸入數據的發送。
  • 步驟二:伺服器會發放用以識別用戶的Session ID。通過驗證從客戶端發送過來的登錄信息進行身份認證,然後把用戶的認證狀態與Session ID 綁定後記錄在伺服器端。

    向客戶端返迴響應時,會在首部欄位Set-Cookie 內寫入Session ID(如PHPSESSID=028a8c…)。

    你可以把Session ID 想像成一種用以區分不同用戶的等位號。然而,如果Session ID 被第三方盜走,對方就可以偽裝成你的身份進行惡意操作了。因此必須防止Session ID 被盜,或被猜出。為了做到這點,Session ID 應使用難以推測的字元串,且伺服器端也需要進行有效期的管理,保證其安全性。

    另外,為減輕跨站腳本攻擊(XSS)造成的損失,建議事先在Cookie 內加上httponly 屬性。
  • 步驟三:客戶端接收到從伺服器端發來的Session ID 後,會將其作為Cookie 保存在本地。下次向伺服器發送請求時,瀏覽器會自動發送Cookie,所以Session ID 也隨之發送到伺服器。伺服器端可通過驗證接收到的Session ID 識別用戶和其認證狀態。

除了以上介紹的應用實例,還有應用其他不同方法的案例。

另外,不僅基於表單認證的登錄信息及認證過程都無標準化的方法,伺服器端應如何保存用戶提交的密碼等登錄信息等也沒有標準化。

通常,一種安全的保存方法是,先利用給密碼加鹽(salt)A 的方式增加額外信息,再使用散列(hash)函數計算出散列值後保存。但是我們也經常看到直接保存明文密碼的做法,而這樣的做法具有導緻密碼泄露的風險。


推薦閱讀:

MaxCompute - ODPS重裝上陣 第三彈 - 複雜類型
流式處理對 請求/響應 正文數據體的影響
Akka HTTP 文檔 (非官方漢化)- 導讀
雲計算的1024種玩法之零基礎入門

TAG:前端开发 | HTTP | 前端工程师 |