標籤:

Cookie與Session機制

會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。常用的會話跟蹤技術是Cookie與Session。Cookie通過在客戶端記錄信息確定用戶身份Session通過在伺服器端記錄信息確定用戶身份

本章將系統地講述Cookie與Session機制。

1.1 Cookie機制

在程序中,會話跟蹤是很重要的事情。理論上,一個用戶的所有請求操作都應該屬於同一個會話,而另一個用戶的所有請求操作則應該屬於另一個會話,二者不能混淆。例如,用戶A在超市購買的任何商品都應該放在A的購物車內,不論是用戶A什麼時間購買的,這都是屬於同一個會話的,不能放入用戶B或用戶C的購物車內,這不屬於同一個會話。

而Web應用程序是使用HTTP協議傳輸數據的。HTTP協議是無狀態的協議。一旦數據交換完畢,客戶端與伺服器端的連接就會關閉,再次交換數據需要建立新的連接。這就意味著伺服器無法從連接上跟蹤會話。即用戶A購買了一件商品放入購物車內,當再次購買商品時伺服器已經無法判斷該購買行為是屬於用戶A的會話還是用戶B的會話了。要跟蹤該會話,必須引入一種機制。

Cookie就是這樣的一種機制。它可以彌補HTTP協議無狀態的不足。在Session出現之前,基本上所有的網站都採用Cookie來跟蹤會話。

1.1.1 什麼是Cookie

Cookie意為「甜餅」,是由W3C組織提出,最早由Netscape社區發展的一種機制。目前Cookie已經成為標準,所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。

由於HTTP是一種無狀態的協議,伺服器單從網路連接上無從知道客戶身份。怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣伺服器就能從通行證上確認客戶身份了。這就是Cookie的工作原理

Cookie實際上是一小段的文本信息。客戶端請求伺服器,如果伺服器需要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給伺服器。伺服器檢查該Cookie,以此來辨認用戶狀態與許可權。伺服器還可以根據需要修改Cookie的內容。

查看某個網站頒發的Cookie很簡單。在瀏覽器地址欄輸入javascript:alert (document. cookie)就可以了(需要有網才能查看)。

注意:Cookie功能需要瀏覽器的支持。如果瀏覽器不支持Cookie(如大部分手機中的瀏覽器)或者把Cookie禁用了,Cookie功能就會失效。不同的瀏覽器採用不同的方式保存Cookie。IE瀏覽器會在「C:Documents and Settings你的用戶名Cookies」文件夾下以文本文件形式保存,一個文本文件保存一個Cookie。

1.1.2 Cookie的不可跨域名性

很多網站都會使用Cookie。例如,Google會向客戶端頒發Cookie,Baidu也會向客戶端頒發Cookie。那瀏覽器訪問Google會不會也攜帶上Baidu頒發的Cookie呢?或者Google能不能修改Baidu頒發的Cookie呢?

答案是否定的。Cookie具有不可跨域名性。根據Cookie規範,瀏覽器訪問Google只會攜帶Google的Cookie,而不會攜帶Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。

Cookie在客戶端是由瀏覽器來管理的。瀏覽器能夠保證Google只會操作Google的Cookie而不會操作Baidu的Cookie,從而保證用戶的隱私安全。瀏覽器判斷一個網站是否能操作另一個網站Cookie的依據是域名。Google與Baidu的域名不一樣,因此Google不能操作Baidu的Cookie。

需要注意的是,雖然網站images.google.com與網站google.com同屬於Google,但是域名不一樣,二者同樣不能互相操作彼此的Cookie。

注意:用戶登錄網站google.com之後會發現訪問images.google.com時登錄信息仍然有效,而普通的Cookie是做不到的。這是因為Google做了特殊處理。本章後面也會對Cookie做類似的處理。

1.1.3 Unicode編碼:保存中文

中文與英文字元不同,中文屬於Unicode字元,在內存中佔4個字元,而英文屬於ASCII字元,內存中只佔2個位元組。Cookie中使用Unicode字元時需要對Unicode字元進行編碼,否則會亂碼。

提示:Cookie中保存中文只能編碼。一般使用UTF-8編碼即可。不推薦使用GBK等中文編碼,因為瀏覽器不一定支持,而且JavaScript也不支持GBK編碼。

1.1.4 BASE64編碼:保存二進位圖片

Cookie不僅可以使用ASCII字元與Unicode字元,還可以使用二進位數據。例如在Cookie中使用數字證書,提供安全度。使用二進位數據時也需要進行編碼。

%注意:本程序僅用於展示Cookie中可以存儲二進位內容,並不實用。由於瀏覽器每次請求伺服器都會攜帶Cookie,因此Cookie內容不宜過多,否則影響速度。Cookie的內容應該少而精。

1.1.5 設置Cookie的所有屬性

除了name與value之外,Cookie還具有其他幾個常用的屬性。每個屬性對應一個getter方法與一個setter方法。Cookie類的所有屬性如表1.1所示。

String name

該Cookie的名稱。Cookie一旦創建,名稱便不可更改

Object value

該Cookie的值。如果值為Unicode字元,需要為字元編碼。如果值為二進位數據,則需要使用BASE64編碼

int maxAge

該Cookie失效的時間,單位秒。如果為正數,則該Cookie在maxAge秒之後失效。如果為負數,該Cookie為臨時Cookie,關閉瀏覽器即失效,瀏覽器也不會以任何形式保存該Cookie。如果為0,表示刪除該Cookie。默認為–1

boolean secure

該Cookie是否僅被使用安全協議傳輸。安全協議。安全協議有HTTPS,SSL等,在網路上傳輸數據之前先將數據加密。默認為false

String path

該Cookie的使用路徑。如果設置為「/sessionWeb/」,則只有contextPath為「/sessionWeb」的程序可以訪問該Cookie。如果設置為「/」,則本域名下contextPath都可以訪問該Cookie。注意最後一個字元必須為「/」

String domain

可以訪問該Cookie的域名。如果設置為「.google.com」,則所有以「google.com」結尾的域名都可以訪問該Cookie。注意第一個字元必須為「.」

String comment

該Cookie的用處說明。瀏覽器顯示Cookie信息的時候顯示該說明

int version

該Cookie使用的版本號。0表示遵循Netscape的Cookie規範,1表示遵循W3C的RFC 2109規範

1.1.6 Cookie的有效期

Cookie的maxAge決定著Cookie的有效期,單位為秒(Second)。Cookie中通過getMaxAge()方法與setMaxAge(int maxAge)方法來讀寫maxAge屬性。

如果maxAge屬性為正數,則表示該Cookie會在maxAge秒之後自動失效。瀏覽器會將maxAge為正數的Cookie持久化,即寫到對應的Cookie文件中。無論客戶關閉了瀏覽器還是電腦,只要還在maxAge秒之前,登錄網站時該Cookie仍然有效。下面代碼中的Cookie信息將永遠有效。

Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie

cookie.setMaxAge(Integer.MAX_VALUE); // 設置生命周期為MAX_VALUE

response.addCookie(cookie); // 輸出到客戶端

如果maxAge為負數,則表示該Cookie僅在本瀏覽器窗口以及本窗口打開的子窗口內有效,關閉窗口後該Cookie即失效。maxAge為負數的Cookie,為臨時性Cookie,不會被持久化,不會被寫到Cookie文件中。Cookie信息保存在瀏覽器內存中,因此關閉瀏覽器該Cookie就消失了。Cookie默認的maxAge值為–1。

如果maxAge為0,則表示刪除該Cookie。Cookie機制沒有提供刪除Cookie的方法,因此通過設置該Cookie即時失效實現刪除Cookie的效果。失效的Cookie會被瀏覽器從Cookie文件或者內存中刪除,

例如:

Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie

cookie.setMaxAge(0); // 設置生命周期為0,不能為負數

response.addCookie(cookie); // 必須執行這一句

response對象提供的Cookie操作方法只有一個添加操作add(Cookie cookie)。

要想修改Cookie只能使用一個同名的Cookie來覆蓋原來的Cookie,達到修改的目的。刪除時只需要把maxAge修改為0即可。

注意:從客戶端讀取Cookie時,包括maxAge在內的其他屬性都是不可讀的,也不會被提交。瀏覽器提交Cookie時只會提交name與value屬性。maxAge屬性只被瀏覽器用來判斷Cookie是否過期。

1.1.7 Cookie的修改、刪除

Cookie並不提供修改、刪除操作。如果要修改某個Cookie,只需要新建一個同名的Cookie,添加到response中覆蓋原來的Cookie。

如果要刪除某個Cookie,只需要新建一個同名的Cookie,並將maxAge設置為0,並添加到response中覆蓋原來的Cookie。注意是0而不是負數。負數代表其他的意義。讀者可以通過上例的程序進行驗證,設置不同的屬性。

注意:修改、刪除Cookie時,新建的Cookie除value、maxAge之外的所有屬性,例如name、path、domain等,都要與原Cookie完全一樣。否則,瀏覽器將視為兩個不同的Cookie不予覆蓋,導致修改、刪除失敗。

1.1.8 Cookie的域名

Cookie是不可跨域名的。域名google.com頒發的Cookie不會被提交到域名百度一下,你就知道去。這是由Cookie的隱私安全機制決定的。隱私安全機制能夠禁止網站非法獲取其他網站的Cookie。

正常情況下,同一個一級域名下的兩個二級域名如helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因為二者的域名並不嚴格相同。如果想所有helloweenvsfei.com名下的二級域名都可以使用該Cookie,需要設置Cookie的domain參數,例如:

Cookie cookie = new Cookie("time","20080808"); // 新建Cookie

cookie.setDomain(".helloweenvsfei.com"); // 設置域名

cookie.setPath("/"); // 設置路徑

cookie.setMaxAge(Integer.MAX_VALUE); // 設置有效期

response.addCookie(cookie); // 輸出到客戶端

讀者可以修改本機C:WINDOWSsystem32driversetc下的hosts文件來配置多個臨時域名,然後使用setCookie.jsp程序來設置跨域名Cookie驗證domain屬性。

注意:domain參數必須以點(".")開始。另外,name相同但domain不同的兩個Cookie是兩個不同的Cookie。如果想要兩個域名完全不同的網站共有Cookie,可以生成兩個Cookie,domain屬性分別為兩個域名,輸出到客戶端。

1.1.9 Cookie的路徑

domain屬性決定運行訪問Cookie的域名,而path屬性決定允許訪問Cookie的路徑(ContextPath)。例如,如果只允許/sessionWeb/下的程序使用Cookie,可以這麼寫:

Cookie cookie = new Cookie("time","20080808"); // 新建Cookie

cookie.setPath("/session/"); // 設置路徑

response.addCookie(cookie); // 輸出到客戶端

設置為「/」時允許所有路徑使用Cookie。path屬性需要使用符號「/」結尾。name相同但domain相同的兩個Cookie也是兩個不同的Cookie。

注意:頁面只能獲取它屬於的Path的Cookie。例如/session/test/a.jsp不能獲取到路徑為/session/abc/的Cookie。使用時一定要注意。

1.1.10 Cookie的安全屬性

HTTP協議不僅是無狀態的,而且是不安全的。使用HTTP協議的數據不經過任何加密就直接在網路上傳播,有被截獲的可能。使用HTTP協議傳輸很機密的內容是一種隱患。如果不希望Cookie在HTTP等非安全協議中傳輸,可以設置Cookie的secure屬性為true。瀏覽器只會在HTTPS和SSL等安全協議中傳輸此類Cookie。下面的代碼設置secure屬性為true:

Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie

cookie.setSecure(true); // 設置安全屬性

response.addCookie(cookie); // 輸出到客戶端

提示:secure屬性並不能對Cookie內容加密,因而不能保證絕對的安全性。如果需要高安全性,需要在程序中對Cookie內容加密、解密,以防泄密。

1.1.11 JavaScript操作Cookie

Cookie是保存在瀏覽器端的,因此瀏覽器具有操作Cookie的先決條件。瀏覽器可以使用腳本程序如JavaScript或者VBScript等操作Cookie。這裡以JavaScript為例介紹常用的Cookie操作。例如下面的代碼會輸出本頁面所有的Cookie。

<script>document.write(document.cookie);</script>

由於JavaScript能夠任意地讀寫Cookie,有些好事者便想使用JavaScript程序去窺探用戶在其他網站的Cookie。不過這是徒勞的,W3C組織早就意識到JavaScript對Cookie的讀寫所帶來的安全隱患並加以防備了,W3C標準的瀏覽器會阻止JavaScript讀寫任何不屬於自己網站的Cookie。換句話說,A網站的JavaScript程序讀寫B網站的Cookie不會有任何結果。

1.1.12 案例:永久登錄

如果用戶是在自己家的電腦上上網,登錄時就可以記住他的登錄信息,下次訪問時不需要再次登錄,直接訪問即可。實現方法是把登錄信息如賬號、密碼等保存在Cookie中,並控制Cookie的有效期,下次訪問時再驗證Cookie中的登錄信息即可。

保存登錄信息有多種方案。最直接的是把用戶名與密碼都保持到Cookie中,下次訪問時檢查Cookie中的用戶名與密碼,與資料庫比較。這是一種比較危險的選擇,一般不把密碼等重要信息保存到Cookie中

還有一種方案是把密碼加密後保存到Cookie中,下次訪問時解密並與資料庫比較。這種方案略微安全一些。如果不希望保存密碼,還可以把登錄的時間戳保存到Cookie與資料庫中,到時只驗證用戶名與登錄時間戳就可以了。

這幾種方案驗證賬號時都要查詢資料庫。

本例將採用另一種方案,只在登錄時查詢一次資料庫,以後訪問驗證登錄信息時不再查詢資料庫。實現方式是把賬號按照一定的規則加密後,連同賬號一塊保存到Cookie中。下次訪問時只需要判斷賬號的加密規則是否正確即可。本例把賬號保存到名為account的Cookie中,把賬號連同密鑰用MD1演算法加密後保存到名為ssid的Cookie中。驗證時驗證Cookie中的賬號與密鑰加密後是否與Cookie中的ssid相等。

2.Session機制

除了使用Cookie,Web應用程序中還經常使用Session來記錄客戶端狀態。Session是伺服器端使用的一種記錄客戶端狀態的機制,使用上比Cookie簡單一些,相應的也增加了伺服器的存儲壓力

2.1 什麼是Session

Session是另一種記錄客戶狀態的機制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在伺服器上。客戶端瀏覽器訪問伺服器的時候,伺服器把客戶端信息以某種形式記錄在伺服器上。這就是Session。客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。

如果說Cookie機制是通過檢查客戶身上的「通行證」來確定客戶身份的話,那麼Session機制就是通過檢查伺服器上的「客戶明細表」來確認客戶身份。Session相當於程序在伺服器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了。

2.2 實現用戶登錄

Session對應的類為javax.servlet.http.HttpSession類。每個來訪者對應一個Session對象,所有該客戶的狀態信息都保存在這個Session對象里。Session對象是在客戶端第一次請求伺服器的時候創建的。Session也是一種key-value的屬性對,通過getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法讀寫客戶狀態信息。Servlet里通過request.getSession()方法獲取該客戶的Session。

注意程序中Session中直接保存了Person類對象與Date類對象,使用起來要比Cookie方便。

當多個客戶端執行程序時,伺服器會保存多個客戶端的Session。獲取Session的時候也不需要聲明獲取誰的Session。Session機制決定了當前客戶只會獲取到自己的Session,而不會獲取到別人的Session。各客戶的Session也彼此獨立,互不可見

提示:Session的使用比Cookie方便,但是過多的Session存儲在伺服器內存中,會對伺服器造成壓力。

2.3 Session的生命周期

Session保存在伺服器端。為了獲得更高的存取速度,伺服器一般把Session放在內存里。每個用戶都會有一個獨立的Session。如果Session內容過於複雜,當大量客戶訪問伺服器時可能會導致內存溢出。因此,Session里的信息應該盡量精簡。

Session在用戶第一次訪問伺服器的時候自動創建。需要注意只有訪問JSP、Servlet等程序時才會創建Session,只訪問HTML、IMAGE等靜態資源並不會創建Session。如果尚未生成Session,也可以使用request.getSession(true)強制生成Session。

Session生成後,只要用戶繼續訪問,伺服器就會更新Session的最後訪問時間,並維護該Session。用戶每訪問伺服器一次,無論是否讀寫Session,伺服器都認為該用戶的Session「活躍(active)」了一次。

2.4 Session的有效期

由於會有越來越多的用戶訪問伺服器,因此Session也會越來越多。為防止內存溢出,伺服器會把長時間內沒有活躍的Session從內存刪除。這個時間就是Session的超時時間。如果超過了超時時間沒訪問過伺服器,Session就自動失效了。

Session的超時時間為maxInactiveInterval屬性,可以通過對應的getMaxInactiveInterval()獲取,通過setMaxInactiveInterval(longinterval)修改。

Session的超時時間也可以在web.xml中修改。另外,通過調用Session的invalidate()方法可以使Session失效。

2.5 Session對瀏覽器的要求

雖然Session保存在伺服器,對客戶端是透明的,它的正常運行仍然需要客戶端瀏覽器的支持。這是因為Session需要使用Cookie作為識別標誌。HTTP協議是無狀態的,Session不能依據HTTP連接來判斷是否為同一客戶,因此伺服器向客戶端瀏覽器發送一個名為JSESSIONID的Cookie,它的值為該Session的id(也就是HttpSession.getId()的返回值)。Session依據該Cookie來識別是否為同一用戶。

該Cookie為伺服器自動生成的,它的maxAge屬性一般為–1,表示僅當前瀏覽器內有效,並且各瀏覽器窗口間不共享,關閉瀏覽器就會失效。

因此同一機器的兩個瀏覽器窗口訪問伺服器時,會生成兩個不同的Session。但是由瀏覽器窗口內的鏈接、腳本等打開的新窗口(也就是說不是雙擊桌面瀏覽器圖標等打開的窗口)除外。這類子窗口會共享父窗口的Cookie,因此會共享一個Session。

注意:新開的瀏覽器窗口會生成新的Session,但子窗口除外。子窗口會共用父窗口的Session。例如,在鏈接上右擊,在彈出的快捷菜單中選擇「在新窗口中打開」時,子窗口便可以訪問父窗口的Session。

如果客戶端瀏覽器將Cookie功能禁用,或者不支持Cookie怎麼辦?例如,絕大多數的手機瀏覽器都不支持Cookie。Java Web提供了另一種解決方案:URL地址重寫。

2.6 URL地址重寫

URL地址重寫是對客戶端不支持Cookie的解決方案。URL地址重寫的原理是將該用戶Session的id信息重寫到URL地址中。伺服器能夠解析重寫後的URL獲取Session的id。這樣即使客戶端不支持Cookie,也可以使用Session來記錄用戶狀態。


推薦閱讀:

你的深度學習應用可能存在安全風險
如何獲取第一手的安全資訊?
一個Tor瀏覽器0day漏洞價值100萬美元,其中的隱情是……

TAG:信息安全 |