Http Cookies 詳解

    HTTP cookies,通常又稱作"cookies",已經存在了很長時間,但是仍舊沒有被予以充分的理解。首要的問題是存在了諸多誤區,認爲cookies是後門程序或病毒,或壓根不知道它是如何工作的。第二個問題是對於cookies缺少一個一致性的接口。儘管存在着這些問題,cookies仍舊在web開發中起着如此重要的作用,以至於如果cookie在沒有可替代品出現的情況下消失,我們許多喜歡的Web應用將變得毫無用處。

       cookies的起源

       早期Web開發面臨的最大問題之一是如何管理狀態。簡言之,服務器端沒有辦法知道兩個請求是否來自於同一個瀏覽器。那時的辦法是在請求的頁面中插入一個token,並且在下一次請求中將這個token返回(至服務器)。這就需要在form中插入一個包含token的隱藏表單域,或着在URLqurey字符串中傳遞該token。這兩種辦法都強調手工操作並且極易出錯。

       Lou Montulli,那時是網景通訊的一個僱員,被認爲在1994年將“magic cookies”的概念應用到了web通訊中。他意圖解決的是web中的購物車,現在所有購物網站都依賴購物車。他的最早的說明文檔提供了一些cookies工作原理的基本信息該文檔在RFC2109中被規範化(這是所有瀏覽器實現cookies的參考依據),並且最終逐步形成了REF2965.Montulli最終也被授予了關於cookies的美國專利。網景瀏覽器在它的第一個版本中就開始支持cookies,並且當前所有web瀏覽器都支持cookies

       cookie是什麼?

       坦白的說,一個cookie就是存儲在用戶主機瀏覽器中的一小段文本文件。Cookies是純文本形式,它們不包含任何可執行代碼。一個Web頁面或服務器告之瀏覽器來將這些信息存儲並且基於一系列規則在之後的每個請求中都將該信息返回至服務器。Web服務器之後可以利用這些信息來標識用戶。多數需要登錄的站點通常會在你的認證信息通過後來設置一個cookie,之後只要這個cookie存在並且合法,你就可以自由的瀏覽這個站點的所有部分。再次,cookie只是包含了數據,就其本身而言並不有害。

       創建cookie

       通過HTTPSet-Cookie消息頭,Web服務器可以指定存儲一個cookieSet-Cookie消息的格式如下面的字符串(中括號中的部分都是可選的)

1 Set-Cookie:value [ ;expires=date][ ;domain=domain][ ;path=path][ ;secure]

      消息頭的第一部分,value部分,通常是一個name=value格式的字符串。事實上,原始手冊指示這是應該使用的格式,但是瀏覽器對cookie的所有值並不會按此格式校驗。實際上,你可以指定一個不包含等號的字符串並且它同樣會被存儲。然而,通常性的使用方式是以name=value的格式(並且多數的接口只支持該格式)來指定cookie的值。

       當一個cookie存在,並且可選條件允許的話,該cookie的值會在接下來的每個請求中被髮送至服務器。cookie值被存儲在名爲CookieHTTP消息頭中,並且只包含了cookie的值,其它的選項全部被去除。例如:        

1 Cookie : value

       通過Set-Cookie指定的選項只是應用於瀏覽器端,一旦選項被設置後便不會被服務器重新取回。cookie的值與Set-Cookie中指定的值是完全一樣的字符串;對於這些值不會有更近一步的解析或轉碼操作。如果在指定的請求中有多個cookies,那麼它們會被分號和空格分開,例如:

1 Cookie:value1 ; value2 ; name1=value1

      服務器端框架通常會提供解析cookies的功能,並且通過編程方式獲取cookies的值。

      cookie編碼(cookie encoding

      對於cookie的值進行編碼一直都存在一些困惑。通常的觀點是cookie的值必須被URL編碼,但是這其實是一個謬誤,儘管可以對cookie的值進行URL編碼。原始的文檔中指示僅有三種類型的字符必須進行編碼:分號,逗號,和空格。規範中提到可以利用URL編碼,但是並不是必須。RFC沒有提及任何的編碼。然而,幾乎所有的實現方式都對cookie的值進行了一些列的URL編碼。對於name=value的格式,namevalue通常都單獨進行編碼並且不對等號“=”進行編碼操作。

      有效期選項(The expires  option

      緊跟cookie值後面的每個選項都以分號和空格分割,並且每個選項都指定cookie何時應該被髮送到服務器。第一個選項是expires,其指定了cookie何時不會再被髮送到服務器端的,因此該cookie可能會被瀏覽器刪掉。該選項所對應的值是一個格式爲Wdy,DD-Mon--YYYY HH:MM:SS GMT的值,例如:

1 Set-Cookie:name=Nicholas;expires=Sat, 02 May 2009 23:38:25 GMT

      在沒有expires選項時,cookie的壽命僅限於單一的會話中。瀏覽器的關閉意味這一次會話的結束,所以會話cookie只存在於瀏覽器保持打開的狀態之下。這就是爲什麼當你登錄到一個web應用時經常看到一個checkbox,詢問你是否選擇存儲你的登錄信息:如果你選擇是的話,那麼一個expires選項會被附加到登錄的cookie中。如果expires選項設置了一個過去的時間點,那麼這個cookie會被立即刪除。

       domain選項(The domain option

      下一個選項是domain,指示cookie將要發送到哪個域或那些域中。默認情況下,domain會被設置爲創建該cookie的頁面所在的域名。例如本站中的cookiedomain屬性的默認值爲www.nczonline.comdomain選項被用來擴展cookie值所要發送域的數量。例如:

1 Set-Cookie:name=Nicholas;domain=nczonline.net

       想象諸如Yahoo這樣的大型網站都會有許多以name.yahoo.com(例如:my.yahoo.com,finance.yahoo.com,等)爲格式的站點。單獨的一個cookie可以簡單的通過將其domain選項設置爲yahoo.com而發送到所有這些站點中。瀏覽器會對domain的值與請求所要發送至的域名,做一個尾部比較(即從字符串的尾部開始比較),並且在匹配後發送一個Cookie消息頭。

      domain設置的值必須是發送Set-Cookie消息頭的域名。例如,我無法向google.com發送一個cookie,因爲這個產生安全問題。不合法的domain選項只要簡單的忽略即可。

        Path選項(The path option

       另一個控制何時發送Cookie消息頭的方式是指定path選項。與domain選項相同的是,path指明瞭在發Cookie消息頭之前必須在請求資源中存在一個URL路徑。這個比較是通過將path屬性值與請求的URL從頭開始逐字符串比較完成的。如果字符匹配,則發送Cookie消息頭,例如:

1 Set-Cookie:name=Nicholas;path=/blog

       在這個例子中,path選項值會與/blog,/blogrool等等相匹配;任何以/blog開頭的選項都是合法的。要注意的是隻有在domain選項覈實完畢之後纔會對path屬性進行比較。path屬性的默認值是發送Set-Cookie消息頭所對應的URL中的path部分。

        secure選項(The  secure  option

      最後一個選項是secure。不像其它選項,該選項只是一個標記並且沒有其它的值。一個secure cookie只有當請求是通過SSLHTTPS創建時,纔會發送到服務器端。這種cookie的內容意指具有很高的價值並且可能潛在的被破解以純文本形式傳輸。例如

1 Set-Cookie:name=Nicholas;secure

      現實中,機密且敏感的信息絕不應該在cookies中存儲或傳輸,因爲cookies的整個機制都是原本不安全的。默認情況下,在HTTPS鏈接上傳輸的cookies都會被自動添加上secure選項。

        cookie的維護和生命週期(cookie maintenance and lifecycle

       任意數量的選項都可以在單一的cookie中指定,並且這些選項可以以任何順序存在,例如

1 Set-Cookie:name=Nicholas; domain=nczonline.net; path=/blog

       這個cooke有四個標識符:cookienamedomainpathsecure標記。要想在將來改變這個cookie的值,需要發送另一個具有相同cookie name,domain,pathSet-Cookie消息頭。例如:

1 Set-Cooke:name=Greg; domain=nczonline.net; path=/blog

       這將以一個新的值來覆蓋原來cookie的值。然而,僅僅只是改變這些選項的某一個也會創建一個完全不同的cookie,例如:

1 Set-Cookie:name=Nicholas; domain=nczonline.net; path=/

        在返回這個消息頭後,會存在兩個同時擁有“name”的不同的cookie。如果你訪問在www.nczonline.NET/blog下的一個頁面,以下的消息頭將被包含進來:

1 Cookie:name=Greg;name=Nicholas

       在這個消息頭中存在了兩個名爲“name”的cookiepath值越詳細則cookie越靠前。domain-path越詳細則cookie字符串越靠前。假設我在ww.nczonline.Net/blog下並且發送了另一個cookie,其設置如下:

1 Set-Cookie:name=Mike

       那麼返回的消息頭現在則變爲:

1 Cookie:name=Mike;name=Greg;name=Nicholas

       由於包含“Mike”的cookie使用了域名(www.nczonline.net)作爲其domain值並且以全路徑(/blog)作爲其path值,則它較其它兩個cookie更加詳細。

    使用失效日期(using expiration dates

       當cookie創建時包含了失效日期,這個失效日期則關聯了以name-domain-path-secure爲標識的cookie。要改變一個cookie的失效日期,你必須指定同樣的組合。當改變一個cookie的值時,你不必每次都設置失效日期,因爲它不是cookie標識信息的組成部分。例如:

1 Set-Cookie:name=Mike;expires=Sat,03 May 2025 17:44:22 GMT

        現在已經設置了cookie的失效日期,所以下次我想要改變cookie的值時,我只需要使用它的名字:

1 Set-Cookie:name=Matt

       在cookie上的失效日期並沒有改變,因爲cookie的標識符是相同的。實際上,只有你手工的改變cookie的失效日期,否則其失效日期不會改變。這意味着在同一個會話中,一個會話cookie可以變成一個持久化cookie(一個可以在多個會話中存在的),反之則不可。爲了要將一個持久化cookie變爲一個會話cookie,你必須刪除這個持久化cookie,這隻要設置它的失效日期爲過去某個時間之後再創建一個同名的會話cookie就可以實現。

       需要記得的是失效日期是以瀏覽器運行的電腦上的系統時間爲基準進行覈實的。沒有任何辦法來來驗證這個系統時間是否和服務器的時間同步,所以當服務器時間和瀏覽器所處系統時間存在差異時這樣的設置會出現錯誤。

       cookie自動刪除(automatic cookie removal

       cookie會被瀏覽器自動刪除,通常存在以下幾種原因:

  • 會話cooke(Session cookie)在會話結束時(瀏覽器關閉)會被刪除
  • 持久化cookiePersistent cookie)在到達失效日期時會被刪除
  • 如果瀏覽器中的cookie限制到達,那麼cookies會被刪除以爲新建cookies創建空間。詳見我的另外一篇關於cookies restrictions的博客

       對於任何這些自動刪除來說,Cookie管理顯得十分重要,因爲這些刪除都是無意識的。

       Cookie限制條件(Cookie restrictions

       在cookies上存在了諸多限制條件,來阻止cookie濫用並保護瀏覽器和服務器免受一些負面影響。有兩種cookies的限制條件:cookies的屬性和cookies的總大小。原始的規範中限定每個域名下不超過20cookies,早期的瀏覽器都遵循該規範,並且在IE7中有個更近一步的提升。在微軟的一次更新中,他們在IE7增加cookies的限制到50個,與此同時Opera限定cookies個數爲30.SafariChrome對與每個域名下的cookies個數沒有限制。

       發向服務器的所有cookies的最大數量(空間)仍舊維持原始規範中所指出的:4KB。所有超出該限制的cookies會被截掉並且不會發送至服務器。

        Subcookies

       鑑於cookie的數量限制,開發者提出的subcookies的觀點來增加cookies的存儲量。Subcookies是一些存儲在一個cookievalue中的一些name-value對,並且通常與以下格式類似:

1 name=a=b&c=d&e=f&g=h

      這種方式允許在單個cookie中保存多個name-value對,而不會超過瀏覽器cookie的數量限制。通過這種方式創建cookies的負面影響是,需要自定義解析方式來提取這些值,相比較而言cookies的格式會更爲簡單。服務器端框架已開始支持subcookies的存儲。我編寫的YUI Cookie utility,支持在JavaScript中讀/寫subcookies

        Javascript中的cookie(cookie In Javascript

       通過Javascript中的document.cookie屬性,你可以創建,維護和刪除cookies。當要創建cookies時該屬性等同於Set-Cookie消息頭,而在讀取cookie時則等同於Cookie消息頭。在創建一個cookie時,你需要使用和Set-Cookie期望格式相同的字符串:

1 document.cookie="name=Nicholas;domain=nczonline.net;path=/";

       設置document.cookie屬性的值並不會刪除存儲在頁面中的所有cookies。它只簡單的創建或修改字符串中指定的cookies。下次發送一個請求到服務器時,這些cookies(通過document.cookie設置的)會和其它通過Set-Cookie消息頭設置的cookies一樣發送至服務器。所有這些cookies並沒有什麼明確的不同之處。

       要使用Javascript提取cookie的值時,只要從document.cookie中讀取即可。返回的字符串與Cookie消息頭中的字符串格式相同,所以多個cookies會被分號和字符串分割。例如:

1 name1=Greg; name2=Nicholas

        鑑於此,你需要手工解析這個cookie字符串來提取真實的cookie數據。當前已有許多描述利用Javascript來解析cookie的資料,包括我的書,Professional Javascript,所以在這我就不再說明。通常利用已存在的Javascript庫操作cookie會更簡單,如YUI Cookie utility 來在Javascript中處理cookies而不要手工重新創建這些算法

        通過訪問document.cookie返回的cookies遵循發向服務器的cookies一樣的訪問規則。要通過Javascript訪問cookies,該頁面和cookies必須在相同的域中,有相同的path,有相同的安全級別。

       注意:一旦cookies通過Javascript設置後遍不能提取它的選項,所以你將不會知道domainpathexpiration日期secure標記。

        HTTP-Only cookies

       微軟的IE6 SP1cookies中引入了一個新的選項:HTTP-only cookies.HTTP-Only背後的意思是告之瀏覽器該cookie不應該通過Javascriptdocument.cookie屬性訪問。設計該特徵意在提供一個安全措施來幫助阻止通過Javascript發起的跨站腳本攻擊(XSS)竊取cookie的行爲(我會在另一篇博客中討論安全問題,本篇如此已足夠)。今Firefox2.0.0.5+Opera9.5+,Chrome都支持HTTP-Only cookies3.2版本的Safari仍不支持。

       要創建一個HTTP-Only cookie,只要向你的cookie中添加一個HTTP-Only標記即可:

1 Set-Cookie: name=Nicholas; HttpOnly

       一旦設定這個標記,通過documen.coookie則不能再訪問該cookieIE同時更近一步並且不允許通過XMLHttpRequestgetAllResponseHeaders()getResponseHeader()方法訪問cookie,然而其它瀏覽器則允許此行爲。Firefox3.0.6修復了該漏洞,然而仍舊有許多瀏覽器漏洞存在,complete browser support list列出了這些。

      你不能通過JavaScript設置HTTP-only cookies,因爲你不能再通過JavaScript讀取這些cookies,這是情理之中的事情。

      總結(conclusion

      爲了高效的利用cookies,仍舊有許多要了解和弄明白的東西。對於一項創建於十多年前但仍舊如最初實現的那樣被使用至今的技術來說,這是件多不可思議的事。本篇只是提供了一些每個人都應該知道的關於瀏覽器cookies的基本指導,但無論如何,也不是一個完整的參考。對於今天的web來說Cookies仍舊起着非常重要的作用,並且不恰當的管理cookies會導致各種安全性的問題,從最糟糕的用戶體驗到安全漏洞。我希望這篇手冊能夠激起一些關於cookies的不可思議的亮點。

     寫在後面的:

     該文章是2009年的,到現在可能已經算一篇比較老的文章,但是文章中關於cookies的講解十分詳細,最近在學習cookies時,讀到該文章,覺得值得大家一讀,同時也作爲自己的參考吧,所以將原文翻譯爲中文。文中較爲詳細的講解了關於cookies的起源,所要解決的問題,以及cookies的本身的屬性和每個屬性的作用,以及相關瀏覽器對於cookies的實現情況和延伸情況,正如作者所表達的那樣,cookies作爲一項十幾年前創建卻一直沿用至今的技術,值得每個開發者思考並瞭解和弄明白關於cookies的一些基本信息和原理,譯文中儘量保證還原原文本意,但水平有限,一些語句翻譯的比較生澀,建議和原文對比閱讀,效果可能會比較好一點。

    關於作者: Nicholas C. Zakas,資深前端工程師,曾在yahoo工作近五年,並擔任前端開發技術領導,他的著作有《javascript 高級程序設計》是一本高質量的前端技術著作。

    注:原文鏈接轉載時請保存原作者鏈接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章