Sep 29

不要/要这样使用Set-Cookie

Lrdcq , 2020/09/29 13:03 , 程序 , 閱讀(1466) , Via 本站原創
在实际项目踩坑过程中,我们主要到Set-Cookie存在多个的情况。有两种做法:

- 一种是一条Set-Cookie,但是这一条中有多个cookie key-value,通过逗号“,”连接。
- 一种是haeder中有多条Set-Cookie字段,

当然也有上面两条组合的情况,但是一般就是这样了。不过这次讨论的是,在实际开发过程中,这两种情况都有踩坑的问题。

哪些坑

1. 首先我们遇到的,部分封装得比较辣鸡的网络相关sdk,在处理response header field的时候,不做处理把key-value全放到一个map(object)中,导致Set-Cookie只保留了一个,产生丢失

具体展开的话,其实也有部分语言的header设计是合理的。比如java的okhttp,的原始response header是一个Array>结构,当然能得到同key,python等内建网络库的语言情况类似。php则更粗暴,通过get_headers()得到是一个Array结构,里面包含的原始header line如"Accept-Ranges: bytes",需要自己去做解析。

还有一些语言和框架虽然给是一个map,但是做了处理。比如试了iOS中的NSURLSession,虽然allHeaderFields是一个NSDictionary,但是实际取出来的字符串是已经用逗号串起来了,也就是说被转换为了单行Set-Cookie的格式。js的情况也类似,fetch也做了相应的处理,只不过实际浏览器中无法访问到set-cookie(https://fetch.spec.whatwg.org/#forbidden-response-header-name),所以用其他header举例子:
點擊在新視窗中瀏覽此圖片

通过js可以得到:
點擊在新視窗中瀏覽此圖片

因此整体来说,这个问题出现在大量的社区/公司内部网络库中,需要方案上的特殊处理,否则容易丢cookie。

2. 另外一个问题,可见大家更多用的,包括刚才转换得到的逗号拼接的Set-Cookie行,但是Set-Cookie单行的情况本身可能出现逗号,主要是“Set-Cookie: lang=; Expires=Sun, 06 Nov 1994 08:49:37 GMT”,标准(非常通用)的Expires格式中,一定会出现逗号。如果一些sdk数据处理,比如“Set-Cookie: lang=; Expires=Sun, 06 Nov 1994 08:49:37 GMT, name=; Expires=Sun, 06 Nov 1994 08:49:37 GMT”,无脑按照逗号拆分,虽然做了容错,也很有可能导致解析中断orcookie的Expires与之后的属性处理异常

这个问题我们在okhttp遇到了:
  /*
     * Split cookie header string according to rfc 2965:
     *   1) split where it is a comma;
     *   2) but not the comma surrounding by double-quotes, which is the comma
     *      inside port list or embeded URIs.
     *
     * @param  header
     *         the cookie header string to split
     *
     * @return  list of strings; never null
     */
    private static List<String> splitMultiCookies(String header) {
        List<String> cookies = new java.util.ArrayList<String>();
        int quoteCount = 0;
        int p, q;

        for (p = 0, q = 0; p < header.length(); p++) {
            char c = header.charAt(p);
            if (c == '"') quoteCount++;
            if (c == ',' && (quoteCount % 2 == 0)) {
                // it is comma and not surrounding by double-quotes
                cookies.add(header.substring(q, p));
                q = p + 1;
            }
        }

        cookies.add(header.substring(q));

        return cookies;
    }

按照set-cookie的行为标准,这反倒是成为一个未定义的行为漏洞了。
当然,其他技术(浏览器,语言,sdk)目前反倒是没发现这个问题。(搜索github应该还是有不少社区代码有这个问题)

怎么做是合理的

判断合理性,最科学的方式是查看http使用规范文档rfc,有一篇专门讲cookie的即6265:https://datatracker.ietf.org/doc/html/rfc6265

1. 具体到setcookie情况,6265中明确的不建议使用一条Set-Cookie header中多个cookie,而应该用多条Set-Cookie。提到的就是","逗号分隔问题,可能会导致cookie的解析异常。
   Origin servers SHOULD NOT fold multiple Set-Cookie header fields into
   a single header field.  The usual mechanism for folding HTTP headers
   fields (i.e., as defined in [RFC2616]) might change the semantics of
   the Set-Cookie header field because the %x2C (",") character is used
   by Set-Cookie in a way that conflicts with such folding.


2. 同样,根据2616所说,https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
   Multiple message-header fields with the same field-name MAY be
   present in a message if and only if the entire field-value for that
   header field is defined as a comma-separated list [i.e., #(values)].
   It MUST be possible to combine the multiple header fields into one
   "field-name: field-value" pair, without changing the semantics of the
   message, by appending each subsequent field-value to the first, each
   separated by a comma. The order in which header fields with the same
   field-name are received is therefore significant to the
   interpretation of the combined field value, and thus a proxy MUST NOT
   change the order of these field values when a message is forwarded.

在header中出现多个key,或者是通过逗号串成一个key,是合理并且等价的。

因此从合理性的角度,其他自定义header如果有需要数组的情况,通过逗号拼接合理性最佳(流量小)。这是需要对value中可能出现的逗号做处理或者转义。
关键词:cookie , set-cookie
logo