通用的cookie编码/解码标准

9
我很难确定编码/解码cookie值的标准(或者是否有标准),与后端平台无关。
根据RFC 2109
“VALUE对用户代理不透明,可以是源服务器选择发送的任何内容,可能采用服务器选择的可打印ASCII编码。‘不透明’意味着该内容仅对源服务器具有重要性和相关性。实际上,任何检查Set-Cookie头的人都可以阅读该内容。”
这听起来像“服务器是老板”,它决定了将应用哪种编码。这使得在从PHP后端设置Cookie并从Python或Java等其他语言中读取Cookie时,不编写任何手动编码/解码处理变得相当困难。
假设我们有一个需要编码的值。俄语/"печенье (*} значения"/表示带有一些额外的非字母数字字符的“cookie value”。 Python: 几乎所有的WSGI服务器都使用Python的SimpleCookie类进行编码/解码,即使许多人表示八进制字面量在ECMA-262严格模式下已被弃用。真是令人费解。
因此,我们的原始cookie值变成了"/\"\320\277\320\265\321\207\320\265\320\275\321\214\320\265 (*} \320\267\320\275\320\260\321\207\320\265\320\275\320\270\321\217\"/"

Node.js:

我完全没有测试,但我猜测使用本地的encodeURIComponentdecodeURIComponent函数的JavaScript后端可以完成它,这些函数使用十六进制转义/取消转义?

PHP:

PHP对cookie值应用urlencode,类似于encodeURIComponent但并不完全相同。

因此,原始值变为:%2F%22%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D1%8C%D0%B5+%28%2A%7D+%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F%22%2F,甚至没有用双引号包装。

然而,如果JavaScript value变量具有上述PHP编码值,则decodeURIComponent(value)会给出 /“печенье+(*}+значения”/ ,请注意+字符而不是空格。 Java、Ruby、Perl和.NET的情况如何?哪种语言遵循(或最接近)所需的行为。实际上,W3是否定义了任何标准?
3个回答

4
我认为您在这里有些混淆了。服务器的编码对客户端没有影响,也不应该有影响。这就是RFC 2109在此处试图表达的内容。
HTTP中的cookie概念类似于现实生活中的情况:当你支付俱乐部的入场费时,你会在手腕上印上一枚墨水印章。这使得你可以离开和重新进入俱乐部而无需再次支付费用。你只需要向保安出示你的手腕即可。在这个现实生活的例子中,你不关心它是什么样子,甚至在正常光线下可能是看不到的-重要的是保安能识别这个东西。如果你把它洗掉,你就失去了再次进入俱乐部而不再付费的特权。
在HTTP中也是同样的原理。服务器与浏览器设置一个cookie。当浏览器返回服务器(即下一个HTTP请求)时,它向服务器显示cookie。服务器识别cookie并相应地处理。这样的cookie可以是诸如“WasHereBefore”标记之类的简单东西。同样,浏览器理解它是什么并不重要。如果你删除你的cookie,服务器将像那个俱乐部的保安一样对待你,好像从来没有见过你一样。
今天,许多cookie只存储一个重要的信息:会话标识符。其他所有数据都存储在服务器端,并与该会话标识符相关联。这个系统的优点是实际数据永远不离开服务器,因此可以信任。客户端存储的所有内容都可以被篡改,因此不应该受到信任。
编辑:阅读了您的评论并再次阅读了您的问题之后,我终于理解了您的情况以及为什么您对cookie的实际编码感兴趣而不是将其留给您的编程语言:如果在同一台服务器上有两个不同的软件环境(例如:Perl和PHP),您可能需要解码由其他语言设置的cookie。在上面的例子中,PHP必须解码Perl cookie或反之亦然。
没有标准规定如何在cookie中存储数据。标准只表示浏览器将精确地发送cookie。所使用的编码方案取决于您的编程语言。回到现实生活的例子中,你现在有两个保安,一个说英语,另一个说俄语。两者必须就一种油墨印章达成一致。很可能至少其中一个人需要学习另一种语言。
由于浏览器行为已经标准化,您可以在所有在您的服务器上使用的语言中模仿一种语言的编码方案,或者在所有使用的语言中创建自己的标准化编码方案。您可能需要使用低级例程,例如PHP的“header()”,而不是高级例程,例如“start_session()”来实现这一点。

顺便提一句,同样地,服务器端编程语言决定如何存储服务器端会话数据。您不能使用PHP的$_SESSION数组访问Perl的CGI::Session


+1 隐形墨水!虽然 cookie 可以很好地用于在同一域上的服务器之间共享结构化数据。 - flup
是的,很好的例子。如果它回答了粗体部分的问题,我很乐意将赏金给予它。无论所携带的数据类型是什么,cookie都应该能够在跨平台上读取...真是令人沮丧和烦恼。 - kirpit
我想我终于理解了你的问题,并相应地编辑了我的答案。 - Hazzit
很好的解释和比喻。+1 - Saeed Neamati

2

无论客户端如何看待cookie,它仍然需要符合HTTP规范。 rfc2616 规定所有HTTP头都应该是ASCII (ISO-8859-1)编码。rfc5987 将其扩展为支持其他字符集,但我不知道它有多广泛的支持。


ASCII是ISO-8859-1的子集(下半部分)。 - flup
@flup,你是对的。如果我正确理解了rfc,它实际上期望ASCII码。 - ykaganovich

0

我更喜欢使用UTF8编码并用base64编码进行包装。它快速、普遍,并且不会在任何一端损坏您的数据。

即使在包装时,您也需要确保明确转换为UTF8。其他语言和运行时虽然支持Unicode,但可能不会将字符串作为UTF8内部存储...就像许多Windows API一样。根据我的经验,Python 2.x很少能正确处理Unicode字符串,除非进行明确转换。

编码:nativeString -> utfEncode() -> base64Encode()

解码:base64Decode() -> utfDecode() -> nativeString

我所知道的几乎每种语言都支持这种方法。您可以寻找通用的单函数编码,但我倾向于谨慎起见选择两步方法...特别是对于外来字符集。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接