cookie中允许使用哪些字符?

cookie名称和值中允许的字符是什么?它们与URL或某个公共子集相同吗?

我问的原因是,我最近遇到了一些奇怪的行为与饼干有-在他们的名字,我只是想知道这是否是浏览器特定的东西或如果我的代码是错误的。

186813 次浏览

我认为这通常是特定于浏览器的。为了安全起见,base64编码了一个JSON对象,并将所有内容存储在其中。这样你只需要解码和解析JSON。base64中使用的所有字符在大多数浏览器(如果不是所有浏览器)中都可以正常运行。

cookie规范有两个版本 1. 版本0 cookie,即Netscape cookie,
2. 版本1即RFC 2965 cookie
在版本0中,cookie的名称和值部分是字符序列,如果不与双引号一起使用,则不包括分号、逗号、等号和空格
版本1要复杂得多,你可以检查在这里
在这个版本中,除了名称不能以$ sign

开头外,名称值部分的规范几乎相同

根据古老的Netscape cookie_spec,整个NAME=VALUE字符串是:

不包括分号、逗号和空格的字符序列。

所以-应该可以工作,它在我这里的浏览器中似乎是OK的;你在哪里有问题?

综上所述:

  • =是合法的,但可能有歧义。浏览器总是将字符串中第一个=符号的名称和值分开,因此在实践中,你可以将=符号放在value中,而不是name中。

这里没有提到什么,因为Netscape在编写规范方面很糟糕,但似乎一直受到浏览器的支持:

  • NAME或VALUE可以是空字符串

  • 如果字符串中根本没有=符号,浏览器将其视为具有空字符串名称的cookie,即Set-Cookie: fooSet-Cookie: =foo相同。

  • 当浏览器输出一个空名称的cookie时,它们省略等号。因此Set-Cookie: =bar产生Cookie: bar

  • 名称和值中的逗号和空格实际上似乎是有效的,尽管等号周围的空格被修剪了

  • 控制字符(\x00\x1F加上\x7F)是不允许的

没有提到的和浏览器完全不一致的是非ascii (Unicode)字符:

  • 在Opera和谷歌Chrome中,它们被编码为UTF-8的Cookie头;
  • 在IE中,使用机器的默认代码页(特定于语言环境,从不使用UTF-8);
  • Firefox(和其他基于mozilla的浏览器)单独使用每个UTF-16代码点的低字节(因此ISO-8859-1是OK的,但其他任何内容都是混乱的);
  • Safari只是拒绝发送任何包含非ascii字符的cookie。

所以实际上你根本不能在cookie中使用非ascii字符。如果你想使用Unicode、控制码或其他任意字节序列,cookie_spec要求你使用自己选择的特别编码方案,并建议url编码(由JavaScript的encodeURIComponent产生)作为合理的选择。

实际标准方面,已经有一些编纂cookie行为的尝试,但到目前为止还没有一个真正反映现实世界。

  • RFC 2109是试图编纂和修复最初的Netscape cookie_spec。在这个标准中,更多的特殊字符是不允许的,因为它使用RFC 2616令牌(这里允许-仍然),并且只有值可以在带引号的字符串中与其他字符一起指定。没有任何浏览器实现过这些限制,对带引号的字符串和转义的特殊处理,或者本规范中的新功能。

  • RFC 2965是另一种尝试,它整理了2109,并在“版本2 cookie”方案下添加了更多功能。也没有人实施过。该规范与早期版本具有相同的标记和引号字符串限制,但它同样是一堆废话。

  • RFC 6265是html5时代清理历史混乱的尝试。它仍然不完全符合实际情况,但比以前的尝试好得多——它至少是浏览器支持的适当子集,没有引入任何应该工作但实际上不起作用的语法(如前面的quote -string)。

在6265中,cookie名称仍然指定为RFC 2616 token,这意味着您可以从字母加上:

!#$%&'*+-.^_`|~

在cookie值中,它正式禁止(浏览器过滤的)控制字符和(不一致实现的)非ascii字符。它保留了cookie_spec对空格、逗号和分号的禁止,加上为了兼容那些真正实现早期rfc的可怜的傻瓜,它还禁止反斜杠和引号,除了引号包装整个值(但在这种情况下,引号仍然被认为是值的一部分,而不是编码方案)。所以剩下的就是字母加上:

!#$%&'()*+-./:<=>?@[]^_`{|}~

在现实世界中,我们仍然使用最原始、最糟糕的Netscape cookie_spec,因此使用cookie的代码应该准备好面对几乎任何情况,但是对于生成cookie的代码,建议坚持使用RFC 6265中的子集。

在ASP。Net中,你可以使用System.Web.HttpUtility在写入cookie之前安全地对cookie值进行编码,并在读取时将其转换回原始形式。

// Encode
HttpUtility.UrlEncode(cookieData);


// Decode
HttpUtility.UrlDecode(encodedCookieData);

这将阻止&号和等号将一个值写入cookie时分割成一堆名称/值对。

几年前,MSIE 5或5.5(可能两者都有)在HTML块中有一些严重的“-”问题,如果你能相信的话。虽然它没有直接的关系,但自从我们在cookie中存储了一个MD5散列(只包含字母和数字)来查找服务器端数据库中的所有其他内容以来。

你不能在cookie的值字段中放入“;”,在大多数浏览器中,将设置的名称是“;”之前的字符串…

更新的rfc6265发布于2011年4月:

cookie-header = "Cookie:" OWS cookie-string OWS
cookie-string = cookie-pair *( ";" SP cookie-pair )
cookie-pair  = cookie-name "=" cookie-value
cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )


cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
; US-ASCII characters excluding CTLs,
; whitespace DQUOTE, comma, semicolon,
; and backslash

如果你看@bobince的答案,你会发现新的限制更严格。

IE和Edge还有一个有趣的问题。名称中包含超过1个句点的cookie似乎会被无声地删除。 所以

cookie_name_a = valuea

而这个会被放弃

cookie.name.a = valuea

在这里,尽量少说。专注于那些不需要逃跑的角色:

饼干:

abdefghijklmnqrstuvxyzABDEFGHIJKLMNQRSTUVXYZ0123456789!#$%&'()*+-./:<>?@[]^_`{|}~

为url

abdefghijklmnqrstuvxyzABDEFGHIJKLMNQRSTUVXYZ0123456789.-_~!$&'()*+,;=:@

对于cookie和url(交集)

abdefghijklmnqrstuvxyzABDEFGHIJKLMNQRSTUVXYZ0123456789!$&'()*+-.:@_~

这就是你的回答。

注意,对于cookie, 已被删除,因为它是 通常用于设置cookie值

对于url this the =被保留。十字路口显然没有。

var chars = "abdefghijklmnqrstuvxyz"; chars += chars.toUpperCase() + "0123456789" + "!$&'()*+-.:@_~";

事实证明,转义仍然会发生,而且还会发生意想不到的情况,特别是在Java cookie环境中,如果遇到最后一个字符,cookie就会用双引号包装。

安全起见,就用A-Za-z1-9。这就是我要做的。

这是简单的:

< p > & lt; cookie-name>可以是除控件以外的任何US-ASCII字符 字符、空格或制表符。它也不能包含 分隔符如下:()<>@,;: \ "/ [] ?= {} . < / p > < p > & lt; cookie-value>可以选择设置在双引号和任何 不包括ctl的US-ASCII字符、空格、双引号、逗号、 允许使用分号、反斜杠。编码:许多实现 执行URL编码的cookie值,但这不是每个 RFC规范。它确实有助于满足关于 哪些字符是允许的

链接:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Directives

还有一点需要注意。我最近实现了一个方案,在这个方案中,一些提交到PHP脚本的敏感数据需要转换并作为加密cookie返回,它使用了我认为保证“安全”的所有base64值。因此,我使用RC4对数据项进行加密,通过base64_encode运行输出,然后愉快地将cookie返回站点。测试似乎很顺利,直到base64编码的字符串包含一个“+”符号。字符串被轻松地写入了页面cookie。使用浏览器诊断,我还可以验证cookie的编写没有改变。然后,当后续页面调用我的PHP并通过$_COOKIE数组获得cookie时,我结结巴巴地发现字符串现在缺少“+”符号。该字符的每一次出现都被替换为一个ASCII空格。

考虑到从那时起,我读到过许多类似的未解决的抱怨,描述这种情况,经常使用base64在cookie中“安全地”存储任意数据,我认为我应该指出问题,并提供我公认的笨拙的解决方案。

在你对一段数据做了任何你想做的加密之后,然后使用base64_encode使它“cookie安全”,通过这个运行输出字符串…

// from browser to PHP. substitute troublesome chars with
// other cookie safe chars, or vis-versa.


function fix64($inp) {
$out =$inp;
for($i = 0; $i < strlen($inp); $i++) {
$c = $inp[$i];
switch ($c) {
case '+':  $c = '*'; break; // definitly won't transfer!
case '*':  $c = '+'; break;


case '=':  $c = ':'; break; // = symbol seems like a bad idea
case ':':  $c = '='; break;


default: continue;
}
$out[$i] = $c;
}
return $out;
}

在这里,我只是将“+”(我还决定用“=”)替换为其他“cookie安全”字符,然后将编码的值返回给页面,作为cookie使用。注意,正在处理的字符串的长度没有改变。当相同的页面(或站点上的另一个页面)再次运行我的PHP脚本时,我将能够在不丢失字符的情况下恢复这个cookie。我只需要记住通过我创建的相同的fix64()调用将cookie传递回去,然后从那里我可以使用通常的base64_decode()解码它,然后是方案中的任何其他解密。

我可以在PHP中做一些设置,允许cookie中使用的base64字符串被传输回PHP而不会损坏。与此同时,这是可行的。“+”可能是一个“合法”的cookie值,但如果您希望能够将这样的字符串传输回PHP(在我的例子中是通过$_COOKIE数组),我建议重新处理以删除违规字符,并在恢复后恢复它们。还有很多其他“饼干安全”的角色可供选择。

最后我用了

cookie_value = encodeURIComponent(my_string);

而且

my_string = decodeURIComponent(cookie_value);

这似乎对各种角色都适用。除此之外,我还遇到了一些奇怪的问题,即使是那些不是分号或逗号的字符。

如果你稍后使用这些变量,你会发现像path这样的东西实际上会让重音字符通过,但它实际上不会匹配浏览器路径。为此,您需要对它们进行URIEncode。比如这样:

  const encodedPath = encodeURI(myPath);
document.cookie = `use_pwa=true; domain=${location.host}; path=${encodedPath};`

因此,“允许的”字符可能比规范中规定的要多。但为了安全起见,您应该遵守规范,并使用uri编码的字符串。