打印

CP9001: Firefox Safari 中对 cookie 中未经编码的中文汉字处理有问题

作者:陆远

标准参考

根据 RFC 2965 规范说明了 cookie 的语法格式:

The two state management headers, Set-Cookie2 and Cookie, have common syntactic properties involving attribute-value pairs. The following grammar uses the notation, and tokens DIGIT (decimal digits), token (informally, a sequence of non-special, non-white space characters), and http_URL from the HTTP/1.1 specification [RFC2616] to describe their syntax.

规范中并没有指明 "非特殊、非空白字符" 是否包含中文汉字这类字符。

关于 cookie 的详细信息,请参考 IETF 组织制定的 "RFC 2965" 规范(HTTP State Management Mechanism)中第 3.1 节的内容。

问题描述

IE Chrome Opera 中,cookie 中可以保存 Unicode 字符;Firefox 则会将中文字符内码将被转换为 Unicode 编码;Safari 会忽略包含中文字符的键值对。

造成的影响

由于 Firefox 及 Safari 对设置 cookie 中包含中文汉字字符时的处理异常,将导致这两个浏览器在取回 cookie 的内容时,得到的字符串异常。

受影响的浏览器

Firefox  
Safari  

问题分析

在本地创建一个 Apache 服务器,创建一个静态页面 cookie.html 放在 Web 服务器上。

代码如下:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<style>
    input { width:80px; height:20px; }
    button { width:70px; height:20px; }
    textarea { width: 150px; height:100px; }
</style>
</head>
<body>
<input type="text" id="text1" /><button id="button1">编码</button><br />
<input type="text" id="text2" /><button id="button2">不编码</button><br />
<textarea id="ta"></textarea>
<script>
    function $ ( id ) {
        return document.getElementById(id);
    }

    function getCookie ( c_name, dec ) {
        if ( document.cookie.length > 0 ) {
            c_start = document.cookie.indexOf(c_name + "=");
            if ( c_start != -1 ) {
                c_start = c_start + c_name.length + 1;
                c_end = document.cookie.indexOf(";", c_start);
                if ( c_end == -1 ) {
                    c_end = document.cookie.length;
                }
                var ret = document.cookie.substring(c_start, c_end);
                return ( dec ) ? unescape(ret) : ret;
            }
        }
        return "";
    }

    function setCookie ( c_name, value, expiredays, enc ) {
        var exdate = new Date();
        exdate.setDate(exdate.getDate() + expiredays);
        var val = ( enc ) ? escape(value) : value;
        document.cookie = c_name + "=" + val
            + ((expiredays==null) ? "" : ";expires=" + exdate.toGMTString());
    }

    $("button1").onclick = function () {
        setCookie("key1", $("text1").value, 365, true);
    }

    window.onload = function () {
        $("text1").value = getCookie("key1", true);
        $("text2").value = getCookie("key2", false);
        $("ta").value = document.cookie;
    }

    $("button2").onclick = function () {
        setCookie("key2", $("text2").value, 365, false);
    }
</script>
</body>
</html>

上面代码中,页面加载后会读取浏览器 cookie 中的内容,将 "key1" 的值先使用 "unescape" 进行解码,之后设置到文本框【text1】中,将 "key2" 的值直接设置到文本框【text2】中。

点击按钮【button1】则先将文本框【text1】中的内容通过"escape"编码,然后保存至 cookie 的 "key1" 下;点击按钮【button2】则直接将文本框【text2】中的内容保存至 cookie 的 "key2" 下。


在各浏览器中打开服务器中测试代码的地址,如:http://localhost/cookie.htm。页面第一次载入后,两个文本框均为空,此时在两个文本框中均输入一段文字,如 "今天ABC123" 。分别点击两个按钮,再刷新页面。

这段代码在不同浏览器中运行结果如下:

IE Chrome Opera Firefox Safari
IE cookie FF cookie SF cookie

可见:

  • IE Chrome Opera 中,cookie 中可以保存 Unicode 字符,所以无论保存至 cookie 的字符是否编码,浏览器均能很好的读取出来;
  • Firefox 中,未编码的包含有中文汉字字符的 cookie 保存时,中文字符内码将被转换为 Unicode 编码(如“今天”的内码为“4ECA 5929”),英文字符则不受影响,在浏览器读取 cookie 时只识别 ASCII 码,导致 Unicode 编码内的高位丢失,造成取出的内容异常(如“今天”高位字符丢失成为“CA 29”编码,对应 ASCII 字符表显示为“Ê)”);
  • Safari 中,未编码的包含有中文汉字字符的 cookie 保存时,由于 cookie 仅可以保存 ASCII 字符,所以浏览器忽略了包含中文字符的键值对,其未被存入 cookie,导致读取时内容丢失。

解决方案

在设置和读取 cookie 时,始终为字符进行编解码操作,推荐使用 "encodeURIComponent" 和 "decodeURIComponent" 方法做相应编解码工作。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.3
Chrome 5.0.375.29 dev
Safari 4.0.5
Opera 10.52
测试页面: cookie.html
本文更新时间: 2010-06-23

关键字

cookie character chinese code escape unescape 乱码 中文 汉字