標籤:實現 開始 就會 set 機制 處理 儲存 網域名稱 display
前言
Cookie是由網景公司的前僱員Lou Montulli 在1993年發明的。向前輩致敬。
1. Cookie和Session介紹
Cookie 和Session 是為了在無狀態下HTTP協議之上維護工作階段狀態,使得伺服器可以知道當前適合哪個客戶在打交道。因為HTTP協議是無狀態的,即每次使用者請求到達伺服器時,HTTP伺服器並不知道這個使用者是誰、是否登入過等。現在的伺服器之所以知道我們是否已經登入,是因為伺服器在登入時設定了瀏覽器的Cookie!Session則是藉由Cookie而實現的更高層的伺服器與瀏覽器之間的會話。
2. Cookie的實現機制
Cookie是由用戶端儲存的小型文字檔,其內容為一系列的索引值對。Cookie是由HTTP伺服器設定的,儲存在瀏覽器中,在使用者訪問其他頁面時,會在HTTP請求中附上該伺服器之前設定的Cookie。Cookie的實現標準定義在RFC2109:HTTP State Management Mechanism中,那麼Cookie是怎麼工作的呢?下面給出整個Cookie的傳遞流程:
1. 瀏覽器(或者用戶端)向某個URL發起HTTP請求(可以是任何請求,比如GET一個頁面、POST一個登入表單等)
2. 對應的HTTP伺服器收到該HTTP請求,並計算應當返回給瀏覽器的HTTP響應。_ps_HTTP響應包括要求標頭和請求體兩部分。
3. 在回應標頭加入Set-Cookie欄位,它的值是要設定的Cookie.
4. 瀏覽器(或者用戶端)收到來自伺服器的HTTP響應。
5. 瀏覽器在回應標頭中發現Set-Cookie欄位,就會將該欄位的值儲存在記憶體或者硬碟中。Set-Cookie 欄位的值可以是很多項Cookie,每一項都可以指定到期的
時間Expires。預設的到期時間是使用者關閉瀏覽器時。
6. 瀏覽器下次給該伺服器發送HTTP請求時,會將伺服器設定的Cookie附加在HTTP請求的頭欄位Cookie中。瀏覽器可以儲存多個網域名稱下的Cookie,但只發送當前
請求的網域名稱曾指定的Cookie,這個網域名稱也可以在set-Cookie欄位中指定。
7. 伺服器收到這個HTTP請求,發現要求標頭中有Cookie欄位,便知道之前就和這個使用者打過交道了。這樣就辨別了瀏覽器(或者用戶端)。
8. 到期的Cookie會被瀏覽器(或者用戶端)刪除。
總之,伺服器通過Set-Cookie回應標頭欄位來指示瀏覽器儲存Cookie,瀏覽器通過Cookie要求標頭欄位來告訴伺服器之前的狀態。Cookie中包含若干個索引值對,每個索引值對都可以設定到期時間。3. Cookie的安全隱患
Cookie提供了一種手段使得HTTP請求可以附加當前的狀態,現在的網站也是靠Cookie來標識使用者的登入狀態的:
1. 使用者提交使用者名稱和密碼的表單,這通常是一個POST HTTP 要求。
2. 伺服器驗證使用者名稱和密碼,如果合法則返回200(OK)並設定Set-Cookie 為 authed = ture 。
3. 瀏覽器儲存該Cookie。
4. 瀏覽器發送請求時,設定Cookie欄位為authed = ture。
5. 伺服器收到第二次請求,從Cookie欄位得知該使用者已經登入。按照已登入使用者的許可權來處理此次請求
這裡面存在的問題在哪?
我們知道可以發送HTTP請求的不只是瀏覽器,很多HTTP用戶端軟體也可以發送任意的HTTP請求,可以設定任何頭欄位。假如我們直接設定Cookie欄位為authed = true
並發送該HTTP請求,伺服器豈不是被欺騙了?這種攻擊非常容易,Cookie是可以被篡改的!
4. Cookie防篡改機制
伺服器可以為每個Cookie項產生簽名,由於使用者篡改Cookie後無法產生對應的簽名, 伺服器便可以得知使用者對Cookie進行了篡改。一個簡單的校正過程可能是這樣的:
- 在伺服器中配置一個不為人知的字串(我們叫它Secret),比如:x$sfz32。
- 當伺服器需要設定Cookie時(比如authed=false),不僅設定authed的值為false, 在值的後面進一步設定一個簽名,最終設定的Cookie是authed=false|6hTiBl7lVpd1P。
- 簽名6hTiBl7lVpd1P是這樣產生的:Hash(‘x$sfz32‘+‘true‘)。 要設定的值與Secret相加再取雜湊。
- 使用者收到HTTP響應並發現頭欄位Set-Cookie: authed=false|6hTiBl7lVpd1P。
- 使用者在發送HTTP請求時,篡改了authed值,設定頭欄位Cookie: authed=true|???。 因為使用者不知道Secret,無法產生簽名,只能隨便填一個。
- 伺服器收到HTTP請求,發現Cookie: authed=true|???。伺服器開始進行校正: Hash(‘true‘+‘x$sfz32‘),便會發現使用者提供的簽名不正確。
通過給Cookie添加簽名,使得伺服器得以知道Cookie被篡改。然而故事並未結束,這樣還是不安全。 因為Cookie是明文傳輸的, 只要伺服器設定過一次authed=true|xxxx我不就知道true的簽名是xxxx了麼, 以後就可以用這個簽名來欺騙伺服器了。因此Cookie中最好不要放敏感性資料。 一般來講Cookie中只會放一個Session Id,而Session儲存在伺服器端
5. Session的實現機制
Session是儲存在伺服器端的,避免了在用戶端Cookie中儲存敏感性資料。Session可以儲存在HTTP伺服器的記憶體中,也可以存在資料庫中,對於重量級的應用甚至可以
儲存在資料庫中。我們以儲存在redis中的Session為例,來考察如何驗證使用者登入狀態的問題。
1. 使用者提交包含使用者名稱和密碼的表單,發送HTTP請求。
2. 伺服器驗證使用者發來的使用者名稱和密碼
3. 如果正確則把目前使用者名(通常是使用者物件)儲存到redis中,並產生它在redis中的ID。 這個ID稱為Session ID,通過Session ID可以從Redis中取出對應的使用者
對象, 敏感性資料(比如authed=true)都儲存在這個使用者物件中。
4. 設定Cookie為sessionId=xxxxxx|checksum
並發送HTTP響應, 仍然為每一項Cookie都設定簽名。
5. 使用者收到HTTP響應後,便看不到任何敏感性資料了。在此後的請求中發送該Cookie給伺服器。
6. 伺服器收到此後的HTTP請求後,發現Cookie中有SessionID,進行放篡改驗證。
7. 如果通過了驗證,根據該ID從Redis中取出對應的使用者物件, 查看該對象的狀態並繼續執行商務邏輯。
Web應用程式框架都是實現上述過程,在Web應用中可以直接擷取目前使用者。相當於在HTTP協議之上,通過Cookie實現了持久的會話。這個會話便稱為Session。
關於HTTP中cookie 和session機制