跳到主要内容

5. 用户代理要求 (User Agent Requirements)

本节足够详细地规定 Cookie 和 Set-Cookie 头, 使精确实现这些要求的用户代理能够与现有 服务器互操作 (即使这些服务器不符合第 4 节描述的行为良好配置文件).

用户代理可以实施比本文规定更多的限制 (例如为了提升安全性). 然而, 实验表明, 这种严格性会降低用户代理与现有服务器互操作的可能性.

5.1. 子组件算法 (Subcomponent Algorithms)

本节定义用户代理用于处理 Cookie 和 Set-Cookie 头中特定子组件的一些算法.

5.1.1. 日期 (Dates)

用户代理 MUST 使用等价于以下算法的算法来解析 cookie-date. 注意, 作为算法一部分定义的 各个布尔标志 (即 found-time, found-day-of-month, found-month, found-year) 初始均为 "not set".

cookie-date = *delimiter date-token-list delimiter date-token-list = date-token ( 1delimiter date-token ) date-token = 1non-delimiter

delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E non-delimiter = %x00-08 / %x0A-1F / DIGIT / ":" / ALPHA / %x7F-FF 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 = 24DIGIT ( non-digit *OCTET ) time = hms-time ( non-digit OCTET ) hms-time = time-field ":" time-field ":" time-field time-field = 12DIGIT

   出现在 cookie-date 中:

time 产生式, 设置 found-time 标志, 并分别将 hour-value, minute-value 和
second-value 设为 date-token 中数字所表示的数值. 跳过其余子步骤并继续处理下一个
date-token.

匹配 day-of-month 产生式, 设置 found-day-of-month 标志, 并将 day-of-month-value
设为 date-token 所表示的数值. 跳过其余子步骤并继续处理下一个 date-token.

month 产生式, 设置 found-month 标志, 并将 month-value 设为 date-token 表示的月份.
跳过其余子步骤并继续处理下一个 date-token.

year 产生式, 设置 found-year 标志, 并将 year-value 设为 date-token 表示的数值.
跳过其余子步骤并继续处理下一个 date-token.

等于 99, 则将 year-value 增加 1900.

等于 69, 则将 year-value 增加 2000.

以不同方式处理.


* found-day-of-month, found-month, found-year 或 found-time 标志中至少一个未设置,

* day-of-month-value 小于 1 或大于 31,

* year-value 小于 1601,

* hour-value 大于 23,

* minute-value 大于 59, 或

* second-value 大于 59.

(注意, 此语法不能表示闰秒.)

年, 时, 分和秒 (UTC) 分别为 day-of-month-value, month-value, year-value,
hour-value, minute-value 和 second-value. 如果不存在这样的日期, 则中止这些步骤并解析
cookie-date 失败.

5.1.2. 规范化主机名 (Canonicalized Host Names)

规范化主机名是由以下算法生成的字符串:

   标签.

转换为 A-label (前者和后者见 [RFC5890] 第 2.3.2.1 节), 或按需转换为
"punycode label" (由 [RFC3490] 第 4 节的 "ToASCII" 转换产生的标签)
(见本规范第 6.3 节).

字符.

5.1.3. 域匹配 (Domain Matching)

如果至少满足以下条件之一, 则称某个字符串 domain-matches 给定域字符串:

o 域字符串与该字符串完全相同. (注意, 此时域字符串和该字符串都已经规范化为小写.)

o 满足以下所有条件:

  *  域字符串是该字符串的后缀.

* 该字符串中未包含在域字符串内的最后一个字符是 %x2E (".") 字符.

* 该字符串是主机名 (即不是 IP 地址).

5.1.4. 路径和路径匹配 (Paths and Path-Match)

用户代理 MUST 使用等价于以下算法的算法计算 cookie 的 default-path:

   部分存在 (否则为空). 例如, 如果 request-uri 只包含路径 (以及可选查询字符串),
则 uri-path 为该路径 (不包含 %x3F ("?") 字符或查询字符串), 如果 request-uri
包含完整 absoluteURI, 则 uri-path 为该 URI 的路径组件.

path 不是 %x2F ("/") 字符, 则输出 %x2F ("/") 并跳过其余步骤.

输出 %x2F ("/") 并跳过其余步骤.

到但不包括最右侧的 %x2F ("/").

如果至少满足以下条件之一, 则 request-path path-matches 给定 cookie-path:

o cookie-path 与 request-path 完全相同.

o cookie-path 是 request-path 的前缀, 且 cookie-path 的最后一个字符是 %x2F ("/").

o cookie-path 是 request-path 的前缀, 且 request-path 中未包含在 cookie-path 内的 第一个字符是 %x2F ("/") 字符.

5.2. Set-Cookie 头 (The Set-Cookie Header)

当用户代理在 HTTP 响应中收到 Set-Cookie 头字段时, 用户代理 MAY 完全忽略该 Set-Cookie 头字段. 例如, 用户代理可能希望阻止对 "third-party" 请求的响应设置 cookie (见第 7.1 节).

如果用户代理没有完全忽略 Set-Cookie 头字段, 用户代理 MUST 将 Set-Cookie 头字段的 field-value 解析为 set-cookie-string (定义如下).

NOTE: 以下算法比第 4.1 节中的语法更宽松. 例如, 该算法会从 cookie 名称和值中剥离 前导和尾随空白 (但保留内部空白), 而第 4.1 节的语法禁止这些位置出现空白. 用户代理 使用该算法是为了与不遵循第 4 节建议的服务器互操作.

用户代理 MUST 使用等价于以下算法的算法来解析 "set-cookie-string":

      name-value-pair 字符串由直到但不包括第一个 %x3B (";") 的字符组成,
unparsed-attributes 由 set-cookie-string 的剩余部分组成 (包括所述 %x3B (";")).

否则:

name-value-pair 字符串由 set-cookie-string 中包含的全部字符组成, unparsed-attributes
为空字符串.

完全忽略 set-cookie-string.

到但不包括第一个 %x3D ("=") 字符, 而 (可能为空的) value 字符串由第一个
%x3D ("=") 字符之后的字符组成.

字符串和值字符串.

完全忽略.

value 字符串.

用户代理 MUST 使用等价于以下算法的算法来解析 unparsed-attributes:

   这些步骤.

将为 %x3B (";") 字符).

字符:

消费 unparsed-attributes 中直到但不包括第一个 %x3B (";") 字符的字符.

否则:

消费 unparsed-attributes 的剩余部分.

令 cookie-av 字符串为此步骤中消费的字符.


(可能为空的) attribute-name 字符串由直到但不包括第一个 %x3D ("=") 字符的字符组成,
而 (可能为空的) attribute-value 字符串由第一个 %x3D ("=") 字符之后的字符组成.

否则:

attribute-name 字符串由整个 cookie-av 字符串组成, attribute-value 字符串为空.

name 字符串和 attribute-value 字符串.

以下小节中的要求. (注意, 具有无法识别 attribute-name 的属性会被忽略.)

当用户代理完成 set-cookie-string 解析时, 称用户代理从 request-uri "receive a cookie", 该 cookie 具有名称 cookie-name, 值 cookie-value, 以及属性 cookie-attribute-list. (接收 cookie 触发的额外要求见第 5.3 节.)

5.2.1. Expires 属性 (The Expires Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "Expires", 用户代理 MUST 按如下方式处理 cookie-av.

令 expiry-time 为将 attribute-value 解析为 cookie-date 的结果 (见第 5.1.1 节).

如果 attribute-value 未能解析为 cookie date, 忽略 cookie-av.

如果 expiry-time 晚于用户代理可表示的最后日期, 用户代理 MAY 将 expiry-time 替换为最后一个可表示日期.

如果 expiry-time 早于用户代理可表示的最早日期, 用户代理 MAY 将 expiry-time 替换为最早可表示日期.

向 cookie-attribute-list 追加一个 attribute-name 为 Expires 且 attribute-value 为 expiry-time 的属性.

5.2.2. Max-Age 属性 (The Max-Age Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "Max-Age", 用户代理 MUST 按如下方式处理 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 追加一个 attribute-name 为 Max-Age 且 attribute-value 为 expiry-time 的属性.

5.2.3. Domain 属性 (The Domain Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "Domain", 用户代理 MUST 按如下方式处理 cookie-av.

如果 attribute-value 为空, 行为未定义. 然而, 用户代理 SHOULD 完全忽略 cookie-av.

如果 attribute-value 字符串的第一个字符是 %x2E ("."):

  令 cookie-domain 为去除前导 %x2E (".") 字符后的 attribute-value.

否则:

  令 cookie-domain 为整个 attribute-value.

将 cookie-domain 转换为小写.

向 cookie-attribute-list 追加一个 attribute-name 为 Domain 且 attribute-value 为 cookie-domain 的属性.

5.2.4. Path 属性 (The Path Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "Path", 用户代理 MUST 按如下方式处理 cookie-av.

如果 attribute-value 为空, 或者 attribute-value 的第一个字符不是 %x2F ("/"):

  令 cookie-path 为 default-path.

否则:

  令 cookie-path 为 attribute-value.

向 cookie-attribute-list 追加一个 attribute-name 为 Path 且 attribute-value 为 cookie-path 的属性.

5.2.5. Secure 属性 (The Secure Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "Secure", 用户代理 MUST 向 cookie-attribute-list 追加一个 attribute-name 为 Secure 且 attribute-value 为空的属性.

5.2.6. HttpOnly 属性 (The HttpOnly Attribute)

如果 attribute-name 以大小写不敏感方式匹配字符串 "HttpOnly", 用户代理 MUST 向 cookie-attribute-list 追加一个 attribute-name 为 HttpOnly 且 attribute-value 为空的属性.

5.3. 存储模型 (Storage Model)

用户代理为每个 cookie 存储以下字段: name, value, expiry-time, domain, path, creation-time, last-access-time, persistent-flag, host-only-flag, secure-only-flag, 以及 http-only-flag.

当用户代理从 request-uri "receives a cookie", 且该 cookie 具有名称 cookie-name, 值 cookie-value 和属性 cookie-attribute-list 时, 用户代理 MUST 按如下方式处理该 cookie:

    例如, 用户代理可能希望阻止从 "third-party" 响应接收 cookie, 或者可能不希望
存储超过某个大小的 cookie.

将 creation-time 和 last-access-time 设置为当前日期和时间.

attribute-name 为 "Max-Age":

将 cookie 的 persistent-flag 设置为 true.

将 cookie 的 expiry-time 设置为 cookie-attribute-list 中最后一个 attribute-name 为
"Max-Age" 的属性的 attribute-value.

否则, 如果 cookie-attribute-list 包含 attribute-name 为 "Expires" 的属性 (且不包含
attribute-name 为 "Max-Age" 的属性):

将 cookie 的 persistent-flag 设置为 true.

将 cookie 的 expiry-time 设置为 cookie-attribute-list 中最后一个 attribute-name 为
"Expires" 的属性的 attribute-value.

否则:

将 cookie 的 persistent-flag 设置为 false.

将 cookie 的 expiry-time 设置为最晚可表示日期.

attribute-name 为 "Domain":

令 domain-attribute 为 cookie-attribute-list 中最后一个 attribute-name 为
"Domain" 的属性的 attribute-value.

否则:

令 domain-attribute 为空字符串.

domain-attribute 是 public suffix:

如果 domain-attribute 与规范化 request-host 完全相同:

令 domain-attribute 为空字符串.

否则:

完全忽略该 cookie 并中止这些步骤.

NOTE: "public suffix" 是由公共注册机构控制的域, 例如 "com", "co.uk" 和
"pvt.k12.wy.us". 此步骤对于防止 attacker.com 通过设置 Domain 属性为 "com" 的
cookie 来破坏 example.com 的完整性至关重要. 遗憾的是, public suffix 集合 (也称为
"registry controlled domains") 会随时间变化. 如果可行, 用户代理 SHOULD 使用最新的
public suffix list, 例如 Mozilla 项目在 `\`http://publicsuffix.org/\`` 维护的列表.


如果规范化 request-host 不 domain-match domain-attribute:

完全忽略该 cookie 并中止这些步骤.

否则:

将 cookie 的 host-only-flag 设置为 false.

将 cookie 的 domain 设置为 domain-attribute.

否则:

将 cookie 的 host-only-flag 设置为 true.

将 cookie 的 domain 设置为规范化 request-host.

attribute-name 为 "Path", 将 cookie 的 path 设置为 cookie-attribute-list 中最后一个
attribute-name 为 "Path" 的属性的 attribute-value. 否则, 将 cookie 的 path 设置为
request-uri 的 default-path.

attribute-name 为 "Secure", 将 cookie 的 secure-only-flag 设置为 true. 否则, 将 cookie 的
secure-only-flag 设置为 false.

attribute-name 为 "HttpOnly", 将 cookie 的 http-only-flag 设置为 true. 否则, 将 cookie 的
http-only-flag 设置为 false.

cookie 的 http-only-flag 已设置, 则中止这些步骤并完全忽略该 cookie.

domain 和 path 与新创建的 cookie 相同:

domain 和 path 与新创建的 cookie 相同. (注意, 此算法保持最多只有一个此类 cookie
的不变式.)

API 且 old-cookie 的 http-only-flag 已设置, 则中止这些步骤并完全忽略新创建的
cookie.

匹配 old-cookie 的 creation-time.

如果 cookie 的过期日期在过去, 则称该 cookie 为 "expired".

如果 cookie 存储中任何时候存在已过期 cookie, 用户代理 MUST 从 cookie 存储中逐出所有已过期 cookie.

如果共享某个 domain 字段的 cookie 数量超过某个由实现定义的上限 (例如 50 个 cookie), 用户代理 MAY 随时从 cookie 存储中 "remove excess cookies".

如果 cookie 存储超过某个预定上限 (例如 3000 个 cookie), 用户代理 MAY 随时从 cookie 存储中 "remove excess cookies".

当用户代理从 cookie 存储中移除多余 cookie 时, 用户代理 MUST 按以下优先级顺序逐出 cookie:

   其他 cookie 的数量.

如果两个 cookie 具有相同移除优先级, 用户代理 MUST 先逐出 last-access 日期最早的 cookie.

当 "the current session is over" (由用户代理定义) 时, 用户代理 MUST 从 cookie 存储中移除 所有 persistent-flag 设置为 false 的 cookie.

5.4. Cookie 头 (The Cookie Header)

用户代理在 Cookie HTTP 请求头中包含已存储 cookie.

当用户代理生成 HTTP 请求时, 用户代理 MUST NOT 附加超过一个 Cookie 头字段.

用户代理 MAY 完全省略 Cookie 头. 例如, 用户代理可能希望在 "third-party" 请求期间阻止发送 cookie (见第 7.1 节).

如果用户代理确实向 HTTP 请求附加 Cookie 头字段, 用户代理 MUST 将 cookie-string (定义如下) 作为该头字段的值发送.

用户代理 MUST 使用等价于以下算法的算法, 从 cookie 存储和 request-uri 计算 "cookie-string":

   满足以下所有要求:

* 要么:

cookie 的 host-only-flag 为 true, 且规范化 request-host 与 cookie 的 domain 完全相同.

要么:

cookie 的 host-only-flag 为 false, 且规范化 request-host domain-matches cookie 的 domain.

* request-uri 的路径 path-matches cookie 的 path.

* 如果 cookie 的 secure-only-flag 为 true, 则 request-uri 的 scheme 必须表示
"secure" 协议 (由用户代理定义).

NOTE: "secure" 协议的概念不由本文档定义. 通常, 如果协议使用传输层安全性
(例如 SSL 或 TLS), 用户代理会认为该协议是安全的. 例如, 大多数用户代理认为
"https" 是表示安全协议的 scheme.

* 如果 cookie 的 http-only-flag 为 true, 且 cookie-string 是为 "non-HTTP" API
(由用户代理定义) 生成的, 则排除该 cookie.

顺序:

* 路径更长的 cookie 列在路径更短的 cookie 之前.

* 在 path 字段长度相等的 cookie 中, creation-time 更早的 cookie 列在 creation-time
更晚的 cookie 之前.

NOTE: 并非所有用户代理都按此顺序排序 cookie-list, 但该顺序反映了本文档编写时的常见实践,
并且历史上确实有服务器 (错误地) 依赖此顺序.

当前日期和时间.

按顺序处理 cookie-list 中的 cookie:

cookie 的值.

字符 %x3B 和 %x20 ("; ").

NOTE: 尽管名为 cookie-string, 它实际上是八位组序列, 而不是字符序列. 为了将 cookie-string (或其组件) 转换为字符序列 (例如用于呈现给用户), 用户代理可能希望尝试使用 UTF-8 字符编码 [RFC3629] 解码该八位组序列. 然而, 这种解码可能失败, 因为并非每个八位组 序列都是有效的 UTF-8.