一、Cookie的出現

瀏覽器和伺服器之間的通信少不了HTTP協議,但是因為HTTP協議是無狀態的,所以伺服器並不知道上一次瀏覽器做了什麼樣的操作,這樣嚴重阻礙了互動式Web應用程序的實現。

針對上述的問題,網景公司的程序員創造了Cookie。

二、Cookie的傳輸

伺服器端在實現Cookie標準的過程中,需要對任意HTTP請求發送Set-Cookie HTTP頭作為響應的一部分:

Set-Cookie: name=value; expires=Tue, 03-Sep-2019 14:10:21 GMT; path=/; domain=.xxx.com;

瀏覽器端會存儲這樣的Cookie,並且為之後的每個請求添加Cookie HTTP請求頭髮送回伺服器:

Cookie: name=value

伺服器通過驗證Cookie值,來判斷瀏覽器發送請求屬於哪一個用戶。

三、瀏覽器中的Cookie

瀏覽器中的Cookie主要由以下幾部分組成:

  • 名稱:Cookie唯一的名稱,必須經過URL編碼處理。(同名會出現覆蓋的情況)
  • 值:必須經過URL編碼處理。
  • 域(domain):默認情況下cookie在當前域下有效,你也可以設置該值來確保對其子域是否有效。
  • 路徑(path):指定Cookie在哪些路徑下有效,默認是當前路徑下。
  • 失效時間(expires):默認情況下,瀏覽器會話結束時會自動刪除Cookie;也可以設置一個GMT格式的日期,指定具體的刪除日期;如果設置的日期為以前的日期,那麼Cookie會立即刪除。
  • 安全標誌(secure):指定之後只允許Cookie發送給https協議。

瀏覽器在發送請求時,只會將名稱與值添加到請求頭的Cookie欄位中,發送給服務端。

瀏覽器提供了一個非常蹩腳的API來操作Cookie:

document.cookie

通過上述方法可以對該Cookie進行寫操作,每一次只能寫入一條Cookie字元串:

document.cookie = a=1; secure; path=/

通過該方法還可以進行Cookie的讀操作:

document.cookie // "a=1"

由於上述方法操作Cookie非常的不直觀,一般都會寫一些函數來簡化Cookie讀取、設置和刪除操作。

對於Cookie的設置操作中,需要以下幾點:

  • 對於名稱和值進行URL編碼處理,也就是採用JavaScript中的encodeURIComponent()方法;
  • expires要求傳入GMT格式的日期,需要處理為更易書寫的方式,比如:設置秒數的方式;
  • 注意只有的屬性名的secure;
  • 每一段信息需要採用分號加空格

function setCookie (key, value, attributes) {
if (typeof document === undefined) {
return
}
attributes = Object.assign({}, {
path: /
}, attributes)

let { domain, path, expires, secure } = attributes

if (typeof expires === number) {
expires = new Date(Date.now() + expires * 1000)
}
if (expires instanceof Date) {
expires = expires.toUTCString()
} else {
expires =
}

key = encodeURIComponent(key)
value = encodeURIComponent(value)

let cookieStr = `${key}=${value}`

if (domain) {
cookieStr += `; domain=${domain}`
}

if (path) {
cookieStr += `; path=${path}`
}

if (expires) {
cookieStr += `; expires=${expires}`
}

if (secure) {
cookieStr += `; secure`
}

return (document.cookie = cookieStr)
}

Cookie的讀操作需要注意的是將名稱與值進行URL解碼處理,也就是調用JavaScript中的decodeURIComponent()方法:

function getCookie (name) {
if (typeof document === undefined) {
return
}
let cookies = []
let jar = {}
document.cookie && (cookies = document.cookie.split(; ))

for (let i = 0, max = cookies.length; i < max; i++) {
let [key, value] = cookies[i].split(=)
key = decodeURIComponent(key)
value = decodeURIComponent(value)
jar[key] = value
if (key === name) {
break
}
}

return name ? jar[name] : jar
}

最後一個清除的方法就更加簡單了,只要將失效日期(expires)設置為過去的日期即可:

function removeCookie (key) {
setCookie(key, , { expires: -1 }) }

介紹Cookie基本操作的封裝之後,還需要了解瀏覽器為了限制Cookie不會被惡意使用,規定了Cookie所佔磁碟空間的大小以及每個域名下Cookie的個數。

為了繞開單域名下Cookie個數的限制,開發人員還創造了一種稱為subcookie的概念,這裡就不在贅述了,可以參考【JavaScript高級程序設計第23章 p633】。

四、服務端的Cookie

相比較瀏覽器端,服務端執行Cookie的寫操作時,是將拼接好的Cookie字元串放入響應頭的Set-Cookie欄位中;執行Cookie的讀操作時,則是解析HTTP請求頭欄位Cookie中的鍵值對。

與瀏覽器最大的不同,在於服務端對於Cookie的安全性操碎了心

signed

當設置signed=true時,服務端會對該條Cookie字元串生成兩個Set-Cookie響應頭欄位:

Set-Cookie: lastTime=2019-03-05T14:31:05.543Z; path=/; httponly
Set-Cookie: lastTime.sig=URXREOYTtMnGm0b7qCYFJ2Db400; path=/; httponly

這裡通過再發送一條以.sig為後綴的名稱以及對值進行加密的Cookie,來驗證該條Cookie是否在傳輸的過程中被篡改。

httpOnly

服務端Set-Cookie欄位中新增httpOnly屬性,當服務端在返回的Cookie信息中含有httpOnly欄位時,開發者是不能通過JavaScript來操縱該條Cookie字元串的。

這樣做的好處主要在於面對XSS(Cross-site scripting)攻擊時,黑客無法拿到設置httpOnly欄位的Cookie信息。

此時,你會發現localStorage相比較Cookie,在XSS攻擊的防禦上就略遜一籌了。

sameSite

在介紹這個新屬性之前,首先你需要明白:當用戶從a.com發起b.com的請求也會攜帶上Cookie,而從a.com攜帶過來的Cookie稱為第三方Cookie。

雖然第三方Cookie有一些好處,但是給CSRF(Cross-site request forgrey)攻擊的機會。

為了從根源上解決CSRF攻擊,sameSite屬性便閃亮登場了,它的取值有以下幾種:

  • strict:瀏覽器在任何跨域請求中都不會攜帶Cookie,這樣可以有效的防禦CSRF攻擊,但是對於有多個子域名的網站採用主域名存儲用戶登錄信息的場景,每個子域名都需要用戶重新登錄,造成用戶體驗非常的差。
  • lax:相比較strict,它允許從三方網站跳轉過來的時候使用Cookie。

為了方便大家理解sameSite的實際效果,可以看這個例子:

// a.com 服務端會在訪問頁面時返回如下Cookie

cookies.set(foo, aaaaa)
cookies.set(bar, bbbbb)
cookies.set(name, cccccc)

// b.com 服務端會在訪問頁面時返回如下Cookie
cookies.set(foo, a, { sameSite: strict })
cookies.set(bar, b, { sameSite: lax })
cookies.set(baz, c)

如何現在用戶在a.com中點擊鏈接跳轉到b.com,它的請求頭是這樣的:

Request Headers

Cookie: bar=b; baz=c

五、網站性能優化

Cookie在服務端和瀏覽器的通信中,主要依靠HTTP的響應頭和請求頭傳輸的,所以Cookie會佔據一定的帶寬。

前面提到瀏覽器會為每一次HTPP請求自動攜帶上Cookie信息,但是對於同站內的靜態資源,伺服器並不需要處理其攜帶的Cookie,這無形中便浪費了帶寬。

在最佳實踐中,一般都會將靜態資源部署到獨立的域名上,從而可以避免無效Cookie的影響。


作者:descire

鏈接:imooc.com/article/28653

來源:慕課網

本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作

推薦閱讀:

相關文章