來源:https://github.com/renaesop/blog/issues/4
RFC 6265 要點翻譯
1.簡介
本文檔定義了HTTP Cookie以及HTTP頭的Set-Cookie字段。通過使用Set-Cookie頭,一個HTTP服務器可以傳遞name/value鍵值對以及相對應的元數據(所謂的cookies)到user agent。當user agent向服務器發送后續請求時,user agent會根據元數據和其他信息來決定是否要在Cookie頭中返回name/value鍵值對。
雖然表面上看起來很簡單, 但時cookies有很多復雜的地方。例如,服務器在向user agent發送cookie時,對每個cookie會設定一個作用域。 作用域制定了user agent回傳cookie的規則:cookie需要回傳的最大期限,需要回傳cookie到哪些服務器,以及需要應用到哪些模式的URI上。
由於歷史原因,cookie包含許多在安全性和隱私上不恰當的地方。例如,服務器可以指定一個給出的cookie字段需要“安全的”連接,但是安全屬性並沒有保證在存在網絡中間人攻擊時cookie的完整性。相似的是,給定host的cookies將會被這個host上的所有端口共享,盡管通常來說,瀏覽器所用的“同源策略”會將從不同端口上取回的東西孤立開來。
這份標准有兩類受眾:會生產cookie的web服務器的開發者,以及會消費cookie的user agent的開發者。
為了最大化在user agent中的通用性,web服務器在生成cookie時應該把他們自身限制為一個在第4章定義的良好的實現者。
User agent必須實現比第5章中定義的更加寬松的規則,以達到最大化和現有的不符合第4章定義的良好實現者的服務器的互通性。
這份文檔說明了在互聯網上經常被使用的頭的句法和語義。特別地,這份文檔並沒有創造新的句法和語義。對cookie生成的推薦標准在第4章提供,表述了一些現有服務器行為的子集,在第5章中表述了一些今天並不被推薦的句法和語義,更加寬松的cookie處理算法。某些現存軟件的實現和推薦的協議有一些重大的不同,這份文檔也包含了一份解釋這些不同的內容。
在這份文檔之前,至少存在着三份不同的cookie描述:所謂的“Netscape cookie 標准”,RFC 2109, RFC2965。然而,這些文檔都沒有描述Cookie和Set-Cookie頭是如何在互聯網上被使用的。根據之前IETF的HTTP狀態管理機制的標准,這份文檔請求下列操作:
[]將RFC2109的狀態改為Historic(已經被RFC2965廢止)
[]將RFC2965的狀態改為Historic
[]指定RFC2965已經被這份文檔廢止
特別的是,通過將RFC2965移到Historic並且將其廢止,這份文檔反對使用Cookie2和Set-Cookie2頭。
2.約定
2.1. 一致標准
此處略去幾百字
2.2. 句法注解
本文檔使用擴充巴科斯范式(ABNF),在RFC5234中有注釋。
下列在RFC5234中定義的核心規則被引用,ALPHA(字母),CR(回車),CRLF(CR LF),CTLs(控制字符),DIGIT(數字0-9),DQUOTE(雙引號),HEXDIG(十六進制元素 0-9/A-F/a-f),LF(換行符),NUL(空八比特),OCTET(除了NUL以外的所有八比特串),SP(空格),HTAB(水平制表符),CHAR(任意ascii碼字符),VCHAR(任意可見的ascii碼字符),以及WSP(空白符)。
OWS(可選空白符)規則被用在0個或更多的線性空白符可能出現的場合:
OWS = *([obs-fold]WSP)
obs-fold = CRLF
OWS應該要么不產生要么就產生為一個單獨的SP字符。
2.3. 術語
下列術語:user agent,client,server,proxy,origin server(源服務器)和HTTP/1.1標准(RFC2616,1.3節)中的含義相同。
術語request-host是指host的名字,也就是已經被user agent所知的,對user agent來說發送HTTP請求的目的地,或者接收HTTP響應的來源。(也就是發送相應的HTTP請求的host名字)。
術語request-uri已經在RFC2616的5.1.2節中定義。
兩個八比特的序列被稱為大小寫不敏感地相同,當且僅當他們在RFC4790中定義的大小寫映射關系被滿足時成立。
術語字符串意思是一個非NUL的八位bit序列。
3.概述
這一節概括了一種源服務器將狀態信息傳遞給user agent的方式,也包含了一種user agent將狀態信息回傳服務器的方式。
為了存儲狀態,源服務器在HTTP響應中包含了一個Set-Cookie頭。在后續的請求中,user agent將回傳一個Cookie請求頭到源服務器。Cookie頭包含了user agent在前面Set-Cookie頭中包含的cookie。源服務器可以選擇忽略Cookie頭或將Cookie用於應用所定義的目的。
源服務器可以在任何響應中發送Set-Cookie響應頭。user agent可以在響應碼為1xx的請求中忽略Set-Cookie,但必須在除此以外的任何種類響應中處理Set-Cookie(包括響應碼為4xx和5xx的響應)。源服務器可以在單個請求的響應中包含多個Set-Cookie字段。Cookie或者Set-Cookie的出現不會阻止存儲和復用HTTP請求的緩存。
源服務器不應該把多個Set-Cookie字段打包到單個HTTP頭中。通常打包HTTP頭的字段可能會更改Set-Cookie字段的語義,因為%x2c(",")字符被Set-Cookie使用,從而在這種打包方式中存在沖突。
3.1. 示例
使用Set-Cookie頭,服務器可以向在一個HTTP響應中user agent發送一條短字符串,這條字符串會在未來符合cookie作用域的HTTP請求中回傳給服務器。例如,服務器可以給user agent發送一個名叫“SID”的“session標識符”,值為 31d4d96e407aad42。user agent會在后續的請求中回傳這個session標識符以及其值。
== Server -> User Agent ==
Set-Cookie: SID=31d4d96e407aad42
== User Agent -> Server ==
Cookie: SID=31d4d96e407aad42
服務器可以使用Path和Domain屬性變更cookie的作用域。例如,服務器可以委托user agent在每個path每個example.com的子域都返回cookie。
== Server -> User Agent ==
Set-Cookie:SID=31d4d96e407aad42;Path=/;Domain=example.com
== User Agent -> Server ==
Cookie: SID=31d4d96e407aad42
就如下一個例子中展示的那樣,服務器可以在user agent中存儲多個cookie。例如,服務器可以通過返回兩個Set-Cookie字段,實現既存儲一個session標識符,又存儲用戶的偏好語言。值得注意的是,服務器用Secure和HttpOnly屬性來對更加敏感的session標識符提供額外的安全保護(見4.1.2.)
== Server -> User Agent ==
Set-Cookie: SID=31d4d96e407aad42; Path=/; Secure; HttpOnly
Set-Cookie: lang=en-US; Path=/; Domain=example.com
== User Agent -> Server ==
Cookie: SID=31d4d96e407aad42; lang=en-US
注意上面的Cookie頭包含了兩個cookie,一個名叫SID,另一個為lang。如果服務器希望cookie在user agent的多個“會話“(sessions,例如,user agent重啟之后)中持續存在,服務器可以在Expires屬性中指定一個過期時間。注意,如果user agent的cookie存儲超過它的定額或者用戶手動刪除了cookie的話,user agent可能會在過期時間到達之前刪除cookie。
== Server -> User Agent ==
Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT
== User Agent -> Server ==
Cookie: SID=31d4d96e407aad42; lang=en-US
最后,為了移除一個cookie,服務器要返回一個把過期時間設置在過去的Set-Cookie字段。服務器只有在Set-Cookie頭中Path和Domain屬性與創建cookie時相符時,才能成功刪除cookie。
== Server -> User Agent ==
Set-Cookie: lang=; Expires=Sun, 06 Nov 1994 08:49:37 GMT
== User Agent -> Server ==
Cookie: SID=31d4d96e407aad42
4. 服務端的要求
本節描述了“表現良好”的Cookie和Set-Cookie頭的句法和語義。
4.1. Set-Cookie
HTTP響應頭中的Set-Cookie被用於從服務器向user agent發送cookie。
4.1.1 句法
不正式地說,Set-Cookie響應頭包含了名字叫做“Set-Cookie”並跟着的一個“:”以及一個cookie。每個cookie由一個name-value鍵值對打頭,后面跟着0個或者多個attribute-value鍵值對。服務器不應該發送未能遵從下列語法的Set-Cookie頭。
set-cookie-header = "Set-Cookie:" SP set-cookie-string
;Set-Cookie: 之后必須有空格,空格之后才是
;具體的set-cookie-string
set-cookie-string = cookie-pair *( ";" SP cookie-av )
;cookie-pair以及每個cookie-av
;之間分隔符都是";" SP(也就是分號加空格)
cookie-pair = cookie-name "=" cookie-value
cookie-name = token
;token表示的是除了分隔符和CTLs以外的ASCII字符
;分隔符包括:
;小中大尖括號 "("|")"|"[]"|"]"|"{"|"}"|"<"|">"
;空格和水平制表符 SP | HT
;逗號分號冒號引號問號等號 ","|";"|":"|"?"|"="|"\""
;斜線: "\" | "/"
;@: "@"
cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
; ASCII字符中除了CTRL(控制字符, 空白符
; 雙引號, 逗號, 分號, 反斜線(\)
token = <token, defined in [RFC2616], Section 2.2>
cookie-av = expires-av / max-age-av / domain-av /
path-av / secure-av / httponly-av /
extension-av
expires-av = "Expires=" sane-cookie-date
sane-cookie-date = <rfc1123-date, defined in [RFC2616], Section 3.3.1>
max-age-av = "Max-Age=" non-zero-digit *DIGIT
; In practice, both expires-av and max-age-av
; are limited to dates representable by the
; user agent.
non-zero-digit = %x31-39
; digits 1 through 9
domain-av = "Domain=" domain-value
domain-value = <subdomain>
; defined in [RFC1034], Section 3.5, as
; enhanced by [RFC1123], Section 2.1
path-av = "Path=" path-value
path-value = <any CHAR except CTLs or ";">
secure-av = "Secure"
httponly-av = "HttpOnly"
extension-av = <any CHAR except CTLs or ";">
注意上面的參考文檔中某些條目使用了一些和這份文檔(ABNF,RFC5234)不同的語法標記。
這份文檔沒有定義任何關於cookie-value的語義。
為了最大化和user agent的兼容性,服務器在要使用一些任意的數據作為cookie-value時,應該將數據編碼,例如使用Base64(RFC4648)。
set-cookie-string中由cookie-av項貢獻的部分是被大家熟知的屬性。為了最大化和user agent的兼容性,服務器不應該產生在set-cookie-string中有兩個屬性相同的名字的cookie。(關於user agent如何處理這種情況,見5.3節)
服務器不應該在同一個響應中包含超過一個具有相同cookie-name的Set-Cookie字段。(關於如何處理這種情況,見5.2節)
如果一個服務器向user agent並發地發送了多條包含Set-Cookie頭的響應(例如,當在多個sockets上和user agent通信時),這些響應將會創造一個“競爭條件”,最終會導致不可預期的后果。
注意:一些現有的user agent對兩位數的年份有不同的處理。為了避免兼容性問題,服務器應該使用要求四位數年份的RFC1123定義的日期格式。
注意:一些user agent會用32位的UNIX time_t來存儲和處理日期。time_t相關的庫的bug可能會導致這些user agent在2038年之后錯誤地處理日期。
4.1.2. 句法(非正式)
本節描述了簡化的關於Set-Cookie頭的語義。這些語義對於要理解最常見的服務器上cookie用法已經足夠詳細。全部地語義在第5節描述。
當user agent接收到一個Set-Cookie頭時,user agent會將cookie及其屬性一起存儲。隨后,當user agent發起HTTP請求時,user agent會在Cookie頭中包含合適的並且沒有過期的cookie。
如果user agent接收到了一個和某個現有cookie的cookie-name、domain-value和path-value都相同的新cookie,現有的那個cookie將會被驅逐,取而代之的是那個新cookie。注意服務器可以通過向user agent發送一個擁有值為過去某一時刻的Expires屬性的新cookie,來刪除一個cookie。
除非cookie的屬性額外指定,cookie將只會回傳到源服務器(例如,不會回傳到任何子域上),並且cookie將會在當前會話結束時過期(會話由user agent自己定義)。user agent會忽略未被識別的cookie屬性(但不會忽略整個cookie)。
4.1.2.1. Expires屬性
Expires屬性指明了cookie的最大生命周期,形式為cookie過期的時刻。user agent並不被要求在設定的時間之前保留cookie。實際上,user agent經常由於存儲壓力或者隱私上的考慮驅逐了cookie。
4.1.2.2. Max-Age屬性
Max-Age屬性指明了cookie的最大生命周期,形式為cookie過期之前的具體秒數。user agent並不被要求在這段指定的時長內保留cookie。實際上,user agent經常由於存儲壓力或者隱私上的考慮驅逐了cookie。
注意:某些現有的user agent並不支持Max-Age屬性。不支持Max-Age屬性的user agent將會直接忽略。
如果cookie既有Max-Age也有Expires屬性,Max-Age屬性將會有更高的優先級,並且控制cookie的過期時間。如果一個cookie既沒有Max-Age也沒有Expires屬性,user agent將會在本次會話(會話由user agent定義)結束之前保留這個cookie。
4.1.2.3. Domain屬性
Domain屬性指明了cookie會被發送到哪些host。例如,如果某cookie的Domain屬性的值為"example.com",user agent將會在向example.com,www.example.com以及www.corp.example.com(注意,最前面的%x2E("."), 如果出現,將會被忽略,盡管它除了出現在末尾以外都是非法的)發送HTTP請求時,在Cookie頭中包含該cookie。如果服務器漏掉了這個Domain屬性,user agent只會向源服務器返回cookie。
警告:某些現存的user agent會將不存在Domain屬性時,錯誤地假設為Domain屬性存在,並且值為當前的host name。例如,如果example.com返回了一個沒有Domain屬性的Set-Cookie頭,這些user agent將會錯誤地也向www.example.com發送cookie。
user agent將會拒絕cookie,除非Domain屬性為cookie指定的作用域會包含源服務器。例如,user agent將會接受一段來自“foo.example.com”的Domain屬性為"example.com"或者"foo.example.com"的cookie,但是user agent不會接受Domain屬性為"bas.example.com"或者"baz.foo.example.com"的cookie。
注意:出於安全的原因,許多user agent被設定為拒絕Domain屬性對應為"公共結尾"的cookie。例如,一些user agent將會拒絕Domain屬性為"com"或"co.uk"等。(詳見5.3節)
4.1.2.4. Path屬性
每個cookie的作用域被限定到了由path組成集合中,由Path屬性控制。如果服務器沒有提供Path屬性,user agent將會使用當前的require-uri中path元素的“目錄”作為默認值(更多細節詳見5.1.4節)
user agent會在一次HTTP請求中包含該cookie,條件是require-uri中路徑的部分匹配Path屬性(或者是Path的子目錄),其中%x2F("/")被解釋為路徑分隔符。
雖然這看起來對分隔同一host中不同路徑的cookie十分實用,但是Path屬性不能作安全的憑據。(見第8節)
4.1.2.5.Secure屬性
Secure屬性將cookie的作用域限定到“安全的”傳輸途徑(“安全的”是有user agent所定義的)。當一個cookie擁有Secure屬性時,user agent只有在請求是從一個安全的傳輸途徑(典型的是以TLS方式傳輸HTTP,也就是HTTPS,RFC2818)傳輸時,才會發送該cookie。
盡管這看起來對保護cookie以免受中間人攻擊很有用,但是Secure屬性只在機密性上保護了cookie。一個網絡中間攻擊者可以通過非安全的傳輸途徑覆蓋該cookie,從而破壞其完整性。(詳見8.6節)
4.1.2.6. HttpOnly屬性
HttpOnly屬性將cookie的作用域限制到HTTP請求中。尤其是,這個屬性委托user agent在提供非HTTP方式訪問cookie時(例如,瀏覽器提供給腳本訪問cookie的接口),忽略該cookie。
4.2. Cookie
4.2.1. 句法
user agent會將存儲的cookie放在Cookie頭中發送給源服務器。如果服務器遵從4.1中的要求(並且user agent遵從第5節的要求),user agent將會發送符合下述語法的Cookie頭部:
cookie-header = "Cookie:" OWS cookie-string OWS
cookie-string = cookie-pair *( ";" SP cookie-pair )
4.2.2. 語義
每個cookie鍵值對都表述了一個被user agent保存的cookie。cookie鍵值對包括從Set-Cookie頭中接收到的的cookie-name和cookie-value。
注意cookie的屬性沒有被返回。尤其是,服務器不能單靠Cookie頭部就能確定,什么時候cookie會過期,cookie對哪些host有效,對什么路徑有效,還有cookie是否設置了Secure或者HttpOnly屬性。
每個單獨的cookie的語義沒有在該文檔中定義。服務器被期望以應用相關地語義來填充cookie。
雖然cookie在Cookie頭中被線性地序列化,但是服務器不應該依賴序列化的順序。尤其是,當Cookie頭中包含了兩個具有相同名字的cookie時(例如,被設置成不同Path或者Domain屬性但擁有相同名字的cookie),服務器不應該依賴這些cookie在頭部中出現的順序。
5. User Agent的要求
本節用具體地細節說明了Cookie和Set-Cookie頭部,使得實現這些要求的user agent可以和現存的服務器(即使那些不滿足第4節中要求的)進行互操作。
5.1. 子元素算法
本節定義了user agent所用的用於處理Cookie和Set-Cookie頭部子元素得一些算法。
5.1.1. 日期
user agent必須使用一個和下述算法等價的算法來實現解析cookie-date。注意下述的各種被定義為算法一部分的boolean-flag(例如,found-time,found-day-of-month, found-month,found-year)最初狀態是“沒有設定”)。
1.使用下述算法,將cookie-date分割成date-token
cookie-date = *delimiter date-token-list *delimiter
date-token-list = date-token *( 1*delimiter date-token )
;date-token由終結符分隔
date-token = 1*non-delimiter
delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E
non-delimiter = %x00-08 / %x0A-1F / DIGIT / ":" / ALPHA /%x7F-FF
;此處的非終結符指的就是非ascii,以及ascii碼中的不可見字符
;以及字母和數字,以及:
;也就是說非終結符在可見ascii字符中只包括數字字母和冒號
non-digit = %x00-2F / %x3A-FF
day-of-month = 1*2DIGIT ( non-digit *OCTET )
month = ( "jan" / "feb" / "mar" / "apr" /
"may" / "jun" / "jul" / "aug" /
"sep" / "oct" / "nov" / "dec" ) *OCTET
year = 2*4DIGIT ( non-digit *OCTET )
time = hms-time ( non-digit *OCTET )
;可以看出時間之后必須要有一個數字來分隔
;但是再往后是什么都無所謂了
hms-time = time-field ":" time-field ":" time-field
time-field = 1*2DIGIT
2.按照下述順序出列每個在cookie-date中出現的token:
1. 如果found-time的flag還沒有設定並且token匹配了time產生式,設定found-time的flag,並且相應地 設定hour、minute、second為token所表示的值。跳過下面剩余的步驟,並繼續處理下一個 date-token。
2. 如果found-day-of-month的flag還沒有被設定,並且token匹配了day-of-month的產生式,設定 found-day-of-month的flag,並且相應地設定day-of-month為token所表示的值。跳過下面剩余的步驟, 並繼續處理下一個date-token。
3. 如果found-month的flag還沒有被設定,並且token匹配了month的產生式,設定found-month的flag, 並且相應地設定month為token所表示的值。跳過下面剩余的步驟,並繼續處理下一個date-token。
4. 如果found-year的flag還沒有被設定,並且token匹配了year的產生式,設定found-year的flag, 並且相應地設定year為token所表示的值。跳過下面剩余的步驟,並繼續處理下一個date-token。
3.如果year的值大於70且小於等於99,在year的值的基礎上增加1900.
4.如果year的值大於0且小於等於69,在year值得基礎上增加2000.
- 注意:某些現有的user agent會用不同的方式解析兩位數的年份。
5.在下列情況中,終止這些步驟並且解析cookie-date以失敗告終:
[*] 至少在found-day-of-month,found-month,found-year,或者found-time的flag中有一個沒有設定
[*] day-of-month的值小於1或者大於31
[*] year的值小於1601
[*] hour的值大於23
[*] minute的值大於59
[*] second的值大於59
(注意在這個句法中閏秒不能被體現)
6.將解析出的cookie-date轉換為UTC時間。如果這個日期不存在,終止算法並且以失敗告終。
7.將轉換出的時間作為算法的結果返回。
5.1.1. 規范化host名
一個規范的host名是通過下述算法生成的字符串:
1. 將host名轉換為獨立域名標簽序列.
2. 將每個不屬於LDH非保留字標簽的標簽,轉換為A標簽詳見[RFC5890] 2.3.2.1),或者轉換為“punycode 標簽”(RFC3490的第四節所定義的“TOASCII”方法).
3. 將最終的標簽序列拼接,中間以%x2E(".")分隔.
5.1.3. Domain匹配
只有在滿足下列條件之一時:
A. domain 字符串和給定的字符串相同。(注意兩者會被規范化為
小寫之后進行比較)
B. 當滿足下列所有條件時:
[*] domain字符串是給定字符串的一個尾部子串
[*] 給定字符串不包含在domain字符串中的最后一位字符是%X2E(".")
[*] 該字符串是一個host name(也就是說,不是一個ip地址)
給定的字符串才匹配給定的domain字符串的domain。
注意此處Domain是指hostname,是不包含端口號的
5.1.4. 路徑和路徑匹配
user agent必須使用和下述算法等價的算法來計算cookie的默認路徑:
1. 將uri-path設定為require-uri中的path部分,如果path不存在, 則設為空。
例如,如果request-uri剛好包含了path(以及可選的query string),那么
uri-path就是該path(不包括%X3F("?")字符或者query string),而當request-uri
包含一個完整的絕對路徑時,uri-path就是URI的path元素。
2. 如果uri-path為空,或者uri-path的第一位不是%X2F("/")字符,輸出%x2F並且
跳過剩余的步驟。
3. 如果uri-path只包含不超過一個%x2F("/"),輸出%x2F並且
跳過剩余的步驟。
4. 輸出uri-path從左邊數第一位到最后一個%x2F("/")之間的字符串,但是結果不
包含最后一個%x2F("/")
至少滿足下面的一個條件時,request-path才匹配cookie-path的路徑:
[*] cookie-path和request-path相等 [*] cookie-path是request-path的前綴,並且cookie-path的最后一位是%x2F("/") [*] cookie-path是request-path的前綴,並且第一個不包含在cookie-path的 字符是%x2F("/")
5.2. Set-Cookie頭
當user agent在一次HTTP響應中接收到一個Set-Cookie字段時,user agent可能會完全會略Set-Cookie字段。例如,user agent可能希望禁止“第三方”cookie(見7.3節)。
如果user agent沒有完全忽略Set-Cookie字段,那么user agent必須解析將Set-Cookie頭中每個字段的值作為cookie-string解析。(下面將定義)
注意:下述算法比4.1節中的語法更加寬松。例如,該算法會將cookie的name-value的頭尾空白符
移除(但保留中間的空白符),不過4.1節中的語法禁止了這些地方的空白符。user agent使用這個算法來和哪些不符合第四節中的推薦的服務器進行互操作。
user agent必須使用和下面所述的算法等價的算法來解析set-cookie-string:
1. 如果set-cookie-string包含%x3B(";"), 那么name-value-pair由set-cookie-string
開頭到第一個%x3B(“;”)組成,但不包含%x3B,並且unparsed-attributes由剩下的set-cookie-string 組成(包括%x3B); 否則,name-value-pair就是set-cookie-string,並且
unparsed-attributes時空串。
2. 如果name-value-pair缺少%x3D("=")字符,那么完全忽略set-cookie-string。
3. name字符串(可能為空)由name-value-pair的開頭,到第一個%x3D("=")組成,但不包含%x3D, 同時value(可能為空)由第一個%x3D之后的所有字符組成。
4. 移除name和value的開頭和結尾處的所有WSP(空白符)。
5. 如果name為空,完全忽略set-cookie-string。
6. cookie-name就是name字符串,cookie-value就是value字符串。
user agent必須使用和下面所述的算法等價的算法來解析unparsed-attributes:
1. 如果unparsed-attributes為空,跳過剩下的這些步驟。
2. 丟棄unparsed-attributes的第一個字符串(也就是%x3B)。
3. 如果剩下的unparsed-attributes中包含%x3B(“;”),那么消耗從開頭到第一個%x3B
的字符串(但不包含);否則,消耗剩下的所有unparsed-attributes。之后,令
這個步驟中消耗的字符串為cookie-av。
4. 如果cookie-av包含一個%x3D(“=”),那么attribute-name(可能為空)由開頭
到第一個%x3D組成,並且不包含%x3D,同時attribute-name(可能為空)為第一個
%x3D之后的字符串;否則,attribute-name的值為整個cookie-av,並且attribute-value
為空。
5. 移除attribute-name和attribute-value的首尾空格。
6. 根據下面子章節所述的要求解析attribute-name和attribute-value(注意,未被
識別的attribute-name將會被忽略)。
7. 回到算法的第一步。
當user agent完成對set-cookie-string的解析時,就說user agent從require-uri中獲取到了
名字為cookie-name,值為cookie-value,以及屬性為cookie-attribute-list的cookie。(由接收到cookie所觸發的額外的要求詳見5.3節)
5.2.1. Expires屬性
如果attribute-name大小寫不敏感地匹配了字符串“Expires”,user agent必須按照下列步驟
處理cookie-av。
令expiry-time的值為將attribute-value按cookie-date(見5.1.1節)解析后的值。
如果attribute-value沒有成功地解析成一個cookie date,那么忽略這個cookie-av。
如果expiry-time晚於user agent可以表達的最晚的時間,user agent可以將expiry-time
替換為user agent可以表達的最晚的時間。
如果expiry-time早於user agent可以表達的最早的時間,user agent可以將expiry-time
替換為能表達的最早的時間。
向cookie-attribute-list追加一個屬性名為Expires,屬性值為expiry-time
的屬性。
5.2.2. Max-Age屬性
如果attribute-name大小寫不敏感地匹配了字符串“Max-Age”,user agent必須按照下列步驟
處理cookie-av。
如果attribute-value的第一個字符不是DIGIT(數字)或者“-”,那么忽略cookie-av。
如果attribute-value剩余的字符中包含一個非DIGIT(非數字)字符,那么忽略這個cookie-av。
令delta-seconds為attribute-value轉換為整數之后的值。
如果delta-seconds小於或等於0,令expiry-time為最早可以表達的日期和時間。否則,
令expiry-time為當前的日期和時間加上delta-seconds的秒數。
向cookie-attribute-list追加一個屬性名為Max-Age,屬性值為expiry-time
的屬性。
5.2.3. Domain屬性
如果attribute-name大小寫不敏感地匹配了字符串“Domain”,user agent必須按照下列步驟
處理cookie-av。
如果attribute-value是空值,那么行為是未知的。然而,user agent應該忽略整條cookie-av。
如果attribute-value的第一個字符是%x2E("."),令cookie-domain為除去attribute-value第一個%x2E(".")之后的值;否則,令cookie-domain的值為整個attribute-value。
將cookie-domain轉換為小寫。
向cookie-attribute-list追加一個屬性名為Domain,屬性值為cookie-domain
的屬性。
5.2.4. Path屬性
如果attribute-name大小寫不敏感地匹配了字符串“Path”,user agent必須按照下列步驟
處理cookie-av。
如果attribute-value時空值或者attribute-value的第一個字符不是%x2F(“/”),
那么令cookie-path為默認路徑;否則,令cookie-path為整個attribute-value。
向cookie-attribute-list追加一個屬性名為Path,屬性值為cookie-path
的屬性。
5.2.5. Secure屬性
如果attribute-name大小寫不敏感地匹配了字符串“Secure”,user agent必須向cookie-attribute-list追加一個屬性名為Secure,屬性值為空的屬性。
5.2.5. HttpOnly屬性
如果attribute-name大小寫不敏感地匹配了字符串“HttpOnly”,user agent必須向cookie-attribute-list追加一個屬性名為HttpOnly,屬性值為空的屬性。
5.3. 存儲模型
user agent的每個cookie會存儲下列所述的字段:name,value,expiry-time,domain,
path,creation-time,last-access-time,persistent-flag,host-only-flag,
secure-only-flag,以及http-only-flag。
當user agent從一個request-uri接受了一個擁有名叫cookie-name,值為cookie-value,
以及屬性為cookie-attribute-list的cookie時,user agent必須像下面這樣處理cookie:
1. user agent可能完全忽略某個接收到的cookie。例如,user agent可能希望禁止掉來自
第三方的cookie,或者user agent不希望存儲超過某個大小的cookie。
2. 新建一個名字為cookie-name,值為cookie-value的新cookie。將creation-time和
last-access-time設定為當前日期和時間。
3. 如果cookie-attribute-list包含一個屬性名為"Max-Age"的屬性,那么將cookie
的presistent-flag設為true,將cookie的expiry-time設定為cookie-attribute-list中
最后一個屬性名為Max-Age的屬性的屬性值;
否則,如果cookie-attribute-list中包含一個屬性名為“Expires”的屬性 (並且不包含“Max-Age”屬性) ,那么將cookie的presistent-flag設定為true,並且將cookie的expiry-time
設定為cookie-attribute-list中最后一個屬性名為Expires的屬性值;
否則,將cookie的presistent-flag屬性設為false,並且將cookie的expiry-time設定為
能表達的最遠的日期。
4. 如果cookie-attribute-list包含一個屬性名為“Domain”的屬性,令domain-attribute
為cookie-attribute-list中最后一個屬性名為Domain的屬性值;否則,令domain-attribute為空串。
5. 如果user agent被配置為拒絕“公共后綴”,並且domain-attribute的值為某個
公共后綴時:如果domain-attribute和規范化后的request-host相同的話,令domain-
attribute屬性為空串;否則,忽略整個cookie病跳過這些步驟。
> 注意: “公共后綴”是一個由公開注冊機構控制的域名,比如說“com”,"co.uk",以及
> "pvt.k12.wy.us"。一個步驟對於預防從attacker.com,通過設置一個Domain屬性
> 為"com"的cookie,來破壞example.com的cookie完整性很重要。不幸的是,
> 公共后綴(著稱的注冊商控制的域名)的集合隨時間發生着變化。如果可能的話,user agent
> 應該用一份最新的公共后綴列表,例如有Mozillas維護的一份列表(https://publicsuffix.org/)。
6. 如果domain-attribute非空:如果規范化之后的request-host不匹配domain-attribute
中的域名,那么完全忽略掉cookie並且終止這些步驟;否則,將cookie的host-only-flag
設定為false,並且將cookie的domain設定為domain-attribute。
否則:將cookie的host-only-flag設定為true,並且將domain設定為規范化之后的request-host。
7. 如果cookie-attribute-list包含一個屬性名為“Path”的屬性,將cookie的path
屬性設定為cookie-attribute-list中最后一個屬性名為“path”的屬性值;否則,將
cookie的path設定為request-uri的默認路徑。(5.1.4節)
8. 如果cookie-attribute-list包含一個屬性名為“Secure”的屬性時,將cookie的
Secure-only-flag設定為true;否則,設為false。
9. 如果cookie-attribute-list包含一個屬性名為“HttpOnly”的屬性時,將cookie的
http-only-flag設定為true;否則,設為false。
10. 如果cookie是從非HTTP的API傳入的,並且設定了cookie的http-only-flag,那么
終止這些步驟並且完全忽略該cookie。
11. 如果cookie的存儲中已經包含了一個和新建的cookie的name、domain、path
都相同的cookie,那么:a. 令old-cookie為現存的domain、name、path都相同的
cookie(注意這個算法保持了至多只有一個這樣cookie的不變性);b. 如果新創建的
cookie是從一個非HTTP的API接收到的並且old-cookie的http-only-flag已經設定,
那么終止這些步驟並且完全忽略這個新創建的cookie;c. 將新創建的cookie的creation-time
更新為old-cookie的creation-time;d. 將old-cookie從cookie 存儲中移除。
12. 將新創建的cookie插入到cookie存儲中。
當cookie的過期時間是在過去時,這個cookie就是“過期的”。
user agent必須從cookie存儲中清除掉所有的過期cookie,條件為在任何時刻,cookie
存儲中存在一個過期的cookie時。
在任何時刻,user agent都可能會從cookie存儲中“移除過量的cookie”,條件為共享
同一個domain字段的cookie的數量超過了預設的邊界值(例如50個cookie)。
在任何時刻,user agent都可能會從cookie存儲中“移除過量的cookie”,條件為所有cookie的總數量超過了預設的邊界值(例如3000個cookie)。
當user agent從cookie存儲中移除過量的cookie時,user agent必須按下面的優先級
清除cookie:
- 過期的cookie
- 共享一個Domain的cookie數目超過其他cookie預設的一個數量(?)時
- 所有的cookie
如果兩個cookie具有相同的移除優先級,那么user agent必須先移除last-access
更早的那個cookie。
當“當前會話結束”(由user agent定義)時,user agent必須從cookie存儲中
移除所有persisitent-flag屬性為false的cookie。
5.4. Cookie頭
user agent將會在HTTP請求的Cookie頭中包含存儲的cookie。
當user agent產生一個HTTP請求時,user agent禁止產生多余一個的Cookie頭字段。
user agent可能會在HTTP中刪除Cookie頭部。例如,user agent可能希望禁止
發送從第三方請求中獲取到的cookie。(見7.1節)
如果user agnet已經把一個Cookie頭部字段添加到HTTP頭部中,user agent必須把cookie-string(由下面定義)當做這個頭部字段的值發送。
user agent必須用一個等價於下面所述的算法來從cookie存儲中結合request-uri,來
計算“cookie-string”:
1. 令cookie-list為cookie存儲中符合下面所有要求的cookie的集合:
[*] 要么,cookie的host-only-flag為true,並且規范化后的request-host和
cookie的域名相同;或者,cookie的host-only-flag為false,並且規范化后
的request-host和cookie的domain匹配;
[*] request-uri的path部分和cookie的path部分相匹配;
[*] 如果cookie的secure-only-flag為true, 那么request-uri的scheme
必須表示一個“安全的”協議(由user agent定義)。
> 注意:“安全的”協議不是由本文檔定義的。典型的情況是,user agent會將使用
安全傳輸的協議認為是安全的,比如SSL或者TLS。例如,大多user agent會將“https”
看作是表示安全協議的scheme。
[*] 如果cookie的http-only-flag被設定為true,那么這個cookie將會在為“非HTTP”
接口(由user agent定義)生產cookie-string時被忽略。
2. user agent應該按照下列順序對cookie-list進行排序:
[*] 擁有更長的path的cookie將會排在擁有更短的path的cookie前面。
[*] 當cookie擁有相同的path字段長度時,擁有更早creation-time的cookie將會
被排在更晚creation-time的cookie前面。
> 注意:不是所有的user agent都按這個吮吸排列cookie-list,但是這個順序
反應了在撰寫本文檔時以及歷史上,現有的服務器(錯誤地)所依賴的順序的經驗。
3. 將cookie-list中每個cookie的last-access-time更新為當前的時刻。
4. 將cookie-list序列化為一個cookie-string,需要按照下列順序處理每個cookie:
1. 輸出cookie的名字,%x3D("=")字符,以及cookie的值.
2. 如果在cookie-list中還存在未處理的cookie,輸出字符%x3B以及%x20("; ").
>注意:除了它的名字,cookie-string其實是一個八bit序列,而不是字符序列。為了將
cookie-string(或者其中的元素)轉換為字符序列(例如,為了展示給用戶),user agent
可能希望嘗試使用UTF-8編碼(RFC3629)來解碼這個八bit序列。但是,這個解碼可能會失敗,因為
不是所有的八bit序列都是合法的UTF-8.
6. 實現上的考慮
6.1. 限制
實際的user agtn實現在他們能存儲的cookie的數目和大小上有限制。常見的user agent
應該提供下述的最小容量:
[] 每個cookie至少4096bytes(以cookie的name,value和attribute之和衡量)
[] 每個domain至少50個cookie
[*] 至少總共3000個cookie
服務器應該使用盡可能小和盡可能小的cookie來防止達到這些實現上的限制,以及最小化
網絡帶寬的需求,因為Cookie頭會在每一個請求中都被包含。
如果user agent未能在Cookie頭中返回一個或者多個cookie,服務器應該優雅地處理
這種情況,因為user agent隨時可能會按照用戶的要求移除任何cookie。
6.2. API
Cookie和Set-Cookie頭使用一個這么難懂的句法的原因是,許多平台(包括服務器和
user agent)都提供了一個基於字符串的有關cookie的API,這就要求應用層的開發者
自己生成和解析Cookie和Set-Cookie頭,這導致許多程序員錯誤地實現了,最終導致
不兼容問題。
為了替代提供基於字符串的cookie相關API的做法,平台最好提供更加語義化的API。推薦
詳細的API設計超過了本文檔的范疇,但是顯然接收一個抽象的"Date"對象而不是一段序列化的
date string會有清晰的好處。
6.3. IDNA依賴與遷移
IDNA2008(RFC5890)取代了IDNA2003(RFC3490)。然而,在兩份說明中存在差異,
因此處理(比如,轉換)從一個標准下注冊的域名標簽到另一個存在差異。IDNA2003存在的
過渡時期會有一定的時常。user agent應該實現IDNA2008(RFC5890)並且可能會實現
UTS46或者RFC5895以加快IDNA的過渡。如果user agent沒有實現IDNA2008,那它應該
實現IDNA2003(RFC3490)。
7. 隱私的考慮
cookie因為允許服務器追蹤用戶飽受批評。例如,很多“web分析”公司使用cookie來識別
用戶是否回到了網站或者訪問了另一個站點。雖然cookie不是服務器唯一可以用來追蹤跨
HTTP請求的手段,但是cookie促進了追蹤,因為他們在user agent的會話之間可以一直存在,
並且可以被多個host共享。
7.1. 第三方cookie
特別令人擔憂的是所謂的“第三方”cookie。在渲染一個HMTL文檔時,user agent經常從其他
服務器請求資源(例如廣告網絡)。這些第三方服務器可以使用cookie來跟蹤用戶,盡管用戶
沒有直接訪問他們的服務器。例如,如果一個用戶訪問了一個帶有第三方內容的網站,之后用戶
瀏覽另一個包含這個內容的網站時,第三方可以跨域兩個站點追蹤用戶。
一些user agent限制了第三方cookie的行為。例如,其中一些user agent拒絕在向第三方的請求中
發送Cookie頭部。另外一些則是拒絕處理第三方請求的響應中的Set-Cookie頭部。user agent在
處理的第三方cookie策略方面有很多不同。本文檔允許user agent在很大范圍內嘗試第三方cookie的策略,來滿足用戶對隱私和兼容性的需求。然而,本文檔並不偏向於任何特別的第三方cookie策略。
禁止第三方cookie的策略,當服務器嘗試繞過這個追蹤用戶的限制來實現他們隱私追蹤時,是無效的。
尤其是,兩個協作的服務器經常通過動態url而不是cookie添加識別信息,來追蹤用戶的時候。
7.2. 用戶控制
user agent應該提供一種給用戶管理存儲在cookie存儲中cookie的機制。例如,user agent可能
讓用戶刪除一段時間內的所有cookie或者和某個domain相關的所有cookie另外,許多user agent
都包含了一個讓用戶檢查存儲在cookie store中的cookie的界面。
user agent應該提供一種可以讓用戶禁用cookie的機制。當cookie被禁用時,user agent禁止在
發出的HTTP請求中包含Cookie頭部,並且user agent處理收到的HTTP響應中的Set-Cookie頭部。
一些user agent提供了阻止cookie跨session存儲的選項。當這個被配置到的時候,user agent
必須將所有收到的cookie當做persistent-flag設定為false進行對待。一些流行的user agent
通過“匿名瀏覽模式”來開放這個功能。
某些user agent提供了允許用戶自己寫入cookie的能力。在大多數常見的場景中,這會產生大量的
對話框。然而,盡管如此,某些看中隱私的用戶認為這個功能很有用。
7.3. 過期時間
雖然服務器可以將cookie的過期時間設定到一個遙遠的未來,但是絕大多數user agent實際上並不會
將cookie保留幾十年。與其選擇無厘頭的很長的國務時間,服務器應該,基於實際目的來選擇一個合適的cookie過期時間,以提高用戶的隱私性。例如,一個典型的cookie標識符應該設置成過期時間為
兩周比較合理。
8. 安全考慮
8.1. 概述
cookie可能有很多安全陷阱。本節概述了幾個比較顯著的問題。
尤其是,cookie鼓勵開發者依賴Ambient Authority做認證,往往提供了被攻擊的弱點,比如說跨站請求偽造(CSRF)。同時,當在cookie中存儲session標識符時,開發者往往也留下了session fixation的隱患。
傳輸層加密,例如使用HTTPS,並不足以防御網絡攻擊者獲得或者更改受攻擊者的cookie,因為cookie協議本身有很多脆弱性(見下面的“弱機密性”和“弱完整性”)。另外,默認情況下,cookie不能從網絡攻擊者那兒獲得完整性和機密性,甚至在使用HTTPS時。
8.2. 環境授權(Ambient Authority)
使用cookie來認證用戶的服務器可能會承受安全上的脆弱性,因為一些user agent允許遠程組織從
user agent發送HTTP請求(例如,通過HTTP重定向或者HTML表單)。當處理這些請求時,user agent會附上這些cookie,盡管遠程組織不知道cookie的內容,但是也潛在地允許遠程組織利用在
沒有防備性的服務器的授權。
盡管這個安全擔憂有過許多名字(比如,跨站請求偽造,confused deputy),這個問題起源於將cookie作為一種環境授權(Ambient Authority)。cookie鼓勵服務器的管理員將名稱(用URL的形式)和授權(cookie)中分離。結果是,user agent可能會向攻擊者指定的地址進行授權,可能導致服務器和客戶端承受由攻擊者指定的動作,因為他們曾經被用戶授權。
服務器管理員可能會考慮通過將URL作為授權表,將名字(URL)和授權綁在一起,來作為取代使用cookie作為授權的手段。取代將secret存在cookie的是,這個方法將secret存在URL中,要求遠程
實體自己提供授權。雖然這個方法不是萬能葯,審慎的運用這個原則可以獲得更好的安全性。
8.3. 明文
除非以安全的途徑傳輸(例如TLS),在Cookie和Set-Cookie字頭是以明文傳輸的。
- 這些頭部中所有傳輸的敏感信息都曝光給了竊聽者。
- 一個懷有惡意的中間人可以更改任一方向中的頭部,帶來不可預知的后果。
- 一個懷有惡意的客戶端可以在傳輸之前更改Cookie頭部,帶來不可預知的后果。
當傳輸到user agent時(就算在安全的隧道發送cookie也是),服務器應該加密並簽名cookie的內容(使用任何服務器想使用的格式)。然而,加密並簽名的cookie內容並沒有預防攻擊者將cookie從一個user agent轉移到另一個,或者之后重放cookie進行攻擊。
簽名和加密每個cookie的內容之外,要求一個更高安全等級的服務器應該只在一個安全的隧道中
使用Cookie和Set-Cookie頭。當使用安全渠道的cookie時,服務器應該設定每個cookie的Secure
屬性。如果服務器沒有設置Secure屬性,由安全隧道提供的保護將會很大程度上的沒有意義。
例如,考慮一個webmail服務器,將session標識符存在一個cookie鍾,並且通過HTTPS訪問。如果
服務器沒有在它的cookie上設定安全屬性,一個活躍的網絡攻擊者將可以攔截任何由user agent發出的HTTP請求並且將其請求重定向到向HTTP上的webmail服務器。就算webmail服務器沒有監聽HTTP連接,user agent也會在該請求中包含cookie。這個活躍的網絡攻擊者攔截這些cookie,並且向服務器重放攻擊,之后獲取到用戶的郵件內容。如果,取而代之,服務器在cookie中設定了Secure屬性,
user agent將會在明文請求中包含這個cookie。
8.4. session標識符
服務器一般在cookie中存儲一個nonce(或者session標識符)來代替,直接在cookie中存儲session信息(可能會被攻擊者獲得或者重放)。當服務器收到一個擁有nonce的HTTP請求時,服務器會把nonce當做key查找和cookie相關聯的狀態信息。
使用session標識符限制了攻擊者可以造成的危害,如果攻擊者拿到了cookie的內容,因為nonce是唯一一個和服務器交互的有用信息(不像非nonce得cookie內容,其本身就是敏感的)。而且,使用單個nonce避免了攻擊者將兩個交互中的內容混雜起來,造成服務器不可預期的行為。
使用session標識符也不是完全沒有風險。例如,服務器應該避免“session fixation”造成的易受攻擊性。固化session攻擊以下面三個步驟進行。首先,攻擊者將session標識符從他的user agent中
移植到受害者的user agent中。第二,受害者使用這個session和服務器交互,可能會用用戶信息填充這個session標識符。第三,攻擊者直接使用這個session標識符和服務器交互,可能會獲得授權信息或者機密信息。
8.5. 弱機密性
cookie沒有提供端口隔離。如果一個cookie在一個端口上是可讀的,那么這個cookie對另一個運行在同一個服務器上不同端口的服務來說也是可讀的。如果一個cookie在一個端口上是可寫的,那么這個cookie對另一個運行在同一個服務器上不同端口的服務來說也是可寫的。出於這個原因,服務器不應該
在具有相同host的不同端口上運行相互不信任的服務,並且用cookie來存儲安全敏感信息。
cookie沒有提供協議帶來的隔離性。雖然絕大多數通常都使用http和HTTPS協議,給定host的cookie也可能被其他協議獲取到,例如ftp和gopher。雖然缺少協議隔離性,對從非HTTP的API獲取cookie的權限是顯著的,但是實際上由協議導致的隔離性缺失表現在了他們需要自己處理cookie(例如,考慮通過HTTP取回一個使用gopher協議的URI)。
cookie往往也沒有提供基於path的隔離性。雖然網絡層的協議並沒有將存儲在一個path的協議發到另一個,但是某些user agent通過非HTTP的api暴露了這些cookie,例如HTML的document.cookie API。因為其中的某些user agent(例如,瀏覽器)並沒有將從不同path獲得的資源隔離起來,從一個path取回的某個資源也可能可以拿到存儲到另一個path的cookie。
8.6. 弱完整性
cookie沒有為兄弟域名(極其子域名)提供完整性保障。例如,考慮foo.example.com和bar.example.com。foo.example.com服務器可以設置一個Domain屬性為"example.com"的cookie(可能覆蓋一個現有的由bar.example.com設置的"example.com"的cookie)。並且user agent會在想bar.example.com的HTTP請求中包含這個cookie。在最壞的情況下,bar.example.com將不能把這個cookie和一個自己設定cookie區分開來。foo.example.com服務器可能會利用這個能力來發起對bar.example.com的攻擊。
盡管Set-Cookie頭支持Path屬性,path屬性也沒有提供任何完整性保證,因為user agent將會接受一個Set-Cookie頭部的任意路徑。例如,一個對 http://example.com/foo/bar 的HTTP響應,可以設置一個Path屬性為"/qux"的cookie。因此,服務器不應該在相同host上的不同path運行兩個相互不信任的服務,並且使用cookie來存儲安全敏感信息。
某個活動的網絡攻擊者也可以向發送到 https://example.com/ 的請求中的Cookie頭部注入cookie,方法是仿造一個 http://example.com/ 的響應並且注入一個Set-Cookie頭。位於example.com的HTTPS服務器將不能分辨gaicooie是否是他自己在HTTPS響應中設置的cookie。一個活躍的網絡攻擊者可能會通過這個機制來攻擊example.com,即使example.com只使用了HTTPS。
譯者注:例如,用戶直接敲了一個HTTP站點,www.baidu.com,在重完成定向到HTTPS之前,中間人可以偽造跳轉,比如返回一個302跳轉到 https://www.baidu.com 同時中間人設定一個cookie
服務器可以通過加密和簽名他們的cookie來緩和這些攻擊。然而,使用密碼學並沒有完全緩解這個問題,因為一個攻擊者可以重放TA從真實的example.com服務器中的sessuib獲取到的cookie,以導致不可預期的后果。
最后,一個攻擊者可以通過存儲大量cookie來強迫user agent刪除掉cookie。一旦user agent達到了它的存儲限制,user agent會被迫驅除掉某些cookie。服務器不應該依賴user agent對cookie的保留。
8.7. 依賴DNS
cookie依賴DNS系統來提供安全性。如果DNS部分或全部受損,cookie協議可能會無法提供應用所要求的安全屬性
9. IANA考慮
永久的消息頭部注冊(RFC3864)已經被下列登記項目更新。
9.1. Cookie
頭部名稱: Cookie
應用協議: http
狀態: 標准
作者/變化控制者: IETF
標准文檔: 本文檔(5.4節)
9.2. Set-Cookie
頭部名稱: Set-Cookie
應用協議: http
狀態: 標准
作者/變化控制者: IETF
標准文檔: 本文檔(5.2節)
9.3. Cookie2
頭部名稱: Cookie2
應用協議: http
狀態: 已淘汰
作者/變化控制者: IETF
標准文檔: RFC2965
9.4. Set-Cookie2
頭部名稱: Set-Cookie2
應用協議: http
狀態: 已淘汰
作者/變化控制者: IETF
標准文檔: RFC2965