每天学习一点点之从 Control character in cookie value or attribute 异常看 Cookie 字符编码限制

文章开头,感谢 @浩哥 在问题排查中的帮助。

背景

昨天,我们接到了来自其他团队的反馈,他们表示在访问我们的服务时,偶尔会遇到 HTTP 500 错误。清除 Cookie 后,服务又恢复正常。根据我们现有的异常处理机制,这种 500 错误大多与登录有关。我们很快就找到了相关的异常信息:

java.lang.IllegalArgumentException: Control character in cookie value or attribute.
	at org.apache.tomcat.util.http.LegacyCookieProcessor.isV0Separator(LegacyCookieProcessor.java:700)
	at org.apache.tomcat.util.http.LegacyCookieProcessor.processCookieHeader(LegacyCookieProcessor.java:498)
	at org.apache.tomcat.util.http.LegacyCookieProcessor.parseCookieHeader(LegacyCookieProcessor.java:227)
	at org.apache.catalina.connector.Request.parseCookies(Request.java:3078)
	at org.apache.catalina.connector.Request.getServerCookies(Request.java:2191)
	at org.apache.catalina.connector.CoyoteAdapter.parseSessionCookiesId(CoyoteAdapter.java:1017)
	at org.apache.catalina.connector.CoyoteAdapter.postParseRequest(CoyoteAdapter.java:730)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:337)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

经过调查,我们发现问题出在 Cookie 中的中文 Value 上。

接下来,我们需要确定是哪个服务设置了这个 Cookie。很快,其他团队的开发人员找到了设置这个 Cookie 的服务。后续就是将这个问题和我们的发现反馈给相关的开发人员。

LegacyCookieProcessor 对 Cookie 的限制

我们发现一个问题:为什么只有我们的服务不支持中文 Cookie,而其他服务却支持呢?

原因在于我们的服务在 2021 年增加了一个配置:

@Configuration
public class CookieConfig {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
        LegacyCookieProcessor legacyCookieProcessor = new LegacyCookieProcessor();
        legacyCookieProcessor.setAllowEqualsInValue(true);
        return (factory) -> factory.addContextCustomizers(
                (context) -> context.setCookieProcessor(legacyCookieProcessor));
    }
}

虽然相关同学已经离职,我们无法知道配置的原因,但可能与处理以 “.” 开头的 domain 有关(https://blog.csdn.net/Dongguabai/article/details/117532407)。现在看来,这是一个无用的配置,我们将在明天进行灰度验证。

我们从异常堆栈中找到报出异常的 isV0Separator 函数。这个函数名中的 “V0” 是指 “Version 0”,也就是 “版本 0”。这是因为 LegacyCookieProcessor 主要遵循的是早期的 Cookie 规范,也就是 Netscape 的规范和 RFC 2109,这两个规范通常被称为 “Version 0” 或 “V0” 规范。

在这些早期的规范中,定义了一些特定的字符作为 Cookie 值的分隔符。isV0Separator 这个函数就是用来检查一个字符是否是这些 V0 规范定义的分隔符。

所以,这个函数名 isV0Separator 表达的意思就是用来检查一个字符是否是 V0 规范的分隔符。

Many HTTP/1.1 header field values consist of words separated by LWS or special characters. These special characters MUST be in a quoted string to be used within a parameter value (as defined in section
3.6).

   token          = 1*<any CHAR except CTLs or separators>
   separators     = "(" | ")" | "<" | ">" | "@"
                  | "," | ";" | ":" | "\" | <">
                  | "/" | "[" | "]" | "?" | "="
                  | "{" | "}" | SP | HT

https://datatracker.ietf.org/doc/html/rfc2616

这段内容来自于 RFC 2616,定义了 HTTP/1.1 协议中的 tokenseparators

  1. token:这是一个由一个或多个字符组成的字符串,这些字符可以是任何字符,除了控制字符(CTLs)和分隔符(separators)。在 ASCII 编码中,控制字符是那些 ASCII 值在 0-31 或者 127 的字符。
  2. separators:这是一组特殊字符,它们在 HTTP/1.1 协议中有特殊的含义。这些字符包括:括号、尖括号、@、逗号、分号、冒号、反斜杠、双引号、斜杠、方括号、问号、等号、大括号、空格和水平制表符。

这段内容的意思是,许多 HTTP/1.1 头字段的值由单词组成,这些单词由空白字符(LWS)或特殊字符分隔。如果这些特殊字符需要在参数值中使用(如在 section 3.6 中定义的那样),那么它们必须被包含在引号内。

RFC 2616 定义了 token 的规则,这个规则被用于很多 HTTP 头部字段值的定义,包括 Cookie 名称。

接下来分析一下 org.apache.tomcat.util.http.LegacyCookieProcessor#isV0Separator

/**
     * Returns true if the byte is a separator as defined by V0 of the cookie
     * spec.
     */
    private static boolean isV0Separator(final char c) {
        if (c < 0x20 || c >= 0x7f) {
            if (c != 0x09) {
                throw new IllegalArgumentException(
                        "Control character in cookie value or attribute.");
            }
        }

        return V0_SEPARATOR_FLAGS.get(c);
    }

这个函数的执行流程是:

  1. 首先,它检查输入的字符 c 是否是一个控制字符。在 ASCII 编码中,小于 0x20(32)或等于或大于 0x7f(127)的字符被认为是控制字符。如果 c 是一个控制字符,并且不是制表符(ASCII 码为 0x09 或 9),那么它会抛出一个 IllegalArgumentException,因为在 Cookie 的值或属性中不允许使用控制字符。

  2. 如果 c 不是一个控制字符,那么它会检查 c 是否是一个 V0 规范的分隔符。这是通过调用 V0_SEPARATOR_FLAGS.get(c) 来完成的,V0_SEPARATOR_FLAGS 是一个 BitSet,它包含了所有有效的 V0 规范的分隔符。如果 c 是一个有效的分隔符,那么 get(c) 方法会返回 true,否则返回 false

        private static final char[] V0_SEPARATORS = {',', ';', ' ', '\t'};
        private static final BitSet V0_SEPARATOR_FLAGS = new BitSet(128);
    		
    		    static {
            for (char c : V0_SEPARATORS) {
                V0_SEPARATOR_FLAGS.set(c);
            }
        }
    

    可以看到函数的执行流程与前面提到的 RFC 2616 对 token 的规范一致。

在 Chrome 种一个中文 Cookie 到 LegacyCookieProcessor 会发生什么

在浏览器中手动设置了一个包含中文的 Cookie,浏览器通常会自动对 Cookie 的值进行 URL 编码,这样就可以将非 ASCII 字符转换为 ASCII 字符。

在 Chrome 设置一个 Cookie:

在这里插入图片描述

发起请求,查看 Request Headers 会发现 test_dongguabai=å¼ ä¸‰;

在这里插入图片描述

这是因为在 Chrome 中设置一个包含中文的 Cookie 时,Chrome 会将非 ASCII 字符进行 URL 编码,自动将中文字符转换为 UTF-8 编码,然后在 HTTP 头部中使用这些字节的 ISO-8859-1 编码的表示。这里看到的“å¼ ä¸‰”是这些字节的 ISO-8859-1 编码的表示,而不是原始的中文字符:

➜  ~ python
Python 3.7.4 (default, Jul  9 2019, 18:13:23) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print("张三".encode('utf-8').decode('latin1')) 
å¼ ä¸‰

在到达 org.apache.tomcat.util.http.LegacyCookieProcessor#processCookieHeader 之前,服务端并不会对 Cookie 的值进行解码,LegacyCookieProcessor 解析的是 URL 编码的字符。

默认使用哪个 CookieProcessor

这里可以查看 org.apache.catalina.core.StandardContext#startInternal

 @Override
    protected synchronized void startInternal() throws LifecycleException {
        ...
        // An explicit cookie processor hasn't been specified; use the default
        if (cookieProcessor == null) {
            cookieProcessor = new Rfc6265CookieProcessor();
        }
        ...
    }

即如果没有配置 cookieProcessor,那么默认为 Rfc6265CookieProcessor

Rfc6265CookieProcessor 与 LegacyCookieProcessor 有什么区别?

Rfc6265CookieProcessorLegacyCookieProcessor 都是 CookieProcessor 的实现,但它们遵循的 Cookie 规范不同。

LegacyCookieProcessor 遵循的是早期的 Cookie 规范,即上文介绍的 V0 规范。

Rfc6265CookieProcessor 从名称就可以看出来,遵循的是 RFC 6265 规范。对 Cookie 的名称和值的限制更加宽松:

cookie-name = token
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
token = <token, defined in [RFC2616], Section 2.2>

https://datatracker.ietf.org/doc/html/rfc6265

token = 1*
separators = “(” | “)” | “<” | “>” | “@”
| “,” | “;” | “:” | “” | <">
| “/” | “[” | “]” | “?” | “=”
| “{” | “}” | SP | HT

https://datatracker.ietf.org/doc/html/rfc2616#section-2.2

  • cookie-name 是一个 token,在 RFC 2616 的 2.2 节中定义。token 是由 1 个或多个字符组成,这些字符可以是任何字符,除了控制字符(CTLs)和分隔符(separators)。分隔符包括一些特殊字符,如括号、尖括号、@、逗号、分号、冒号、反斜杠、双引号、斜杠、方括号、问号、等号、大括号、空格(SP)和水平制表符(HT)。
  • cookie-value 可以是多个 cookie-octet,或者是被双引号包围的多个 cookie-octet
  • cookie-octet 是一个 US-ASCII 字符,除了控制字符(CTLs)、空白、双引号、逗号、分号和反斜杠之外的任何字符。具体来说,它可以是以下 ASCII 码值的字符:
    • %x21(即 33,对应的字符是 !)
    • %x23-2B(即 35-43,对应的字符是 # 到 +)
    • %x2D-3A(即 45-58,对应的字符是 - 到 :)
    • %x3C-5B(即 60-91,对应的字符是 < 到 [)
    • %x5D-7E(即 93-126,对应的字符是 ] 到 ~)

这些规则限制了 Cookie 名称和值中可以使用的字符。org.apache.tomcat.util.http.Rfc6265CookieProcessor#validateCookieValue 就是对以上规范的实现:

    private void validateCookieValue(String value) {
        int start = 0;
        int end = value.length();

        if (end > 1 && value.charAt(0) == '"' && value.charAt(end - 1) == '"') {
            start = 1;
            end--;
        }

        char[] chars = value.toCharArray();
        for (int i = start; i < end; i++) {
            char c = chars[i];
            if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
                throw new IllegalArgumentException(sm.getString(
                        "rfc6265CookieProcessor.invalidCharInValue", Integer.toString(c)));
            }
        }
    }

根据 RFC 6265 规范,Cookie 的名称和值只能包含 US-ASCII 字符,这意味着它们不能直接包含中文字符。但是部分浏览器会进行 URL 编码,从而可以实现在 Cookie 中存储中文字符。

References

  • https://datatracker.ietf.org/doc/html/rfc2616
  • https://blog.csdn.net/Dongguabai/article/details/117532407
  • https://datatracker.ietf.org/doc/html/rfc6265
  • https://datatracker.ietf.org/doc/html/rfc2616#section-2.2

欢迎关注公众号:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/196752.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

房屋租赁出售经纪人入驻小程序平台

一款专为房屋中介开发的小程序平台&#xff0c;支持独立部署&#xff0c;源码交付&#xff0c;数据安全无忧。 核心功能&#xff1a;房屋出租、经纪人独立后台、分佣后台、楼盘展示、房型展示、在线咨询、地址位置配套设施展示。 程序已被很多房屋交易中介体验使用过&#x…

华为鸿蒙开发(HarmonyOs开发):超详细的:DevEco Studio 的安装和配置 、华为第三方包依赖:SDK软件包的安装、Nodejs的导入配置

2023年11月28日20:00:00 ⚠️⚠️HarmonyOs 开发工具 ⚠️⚠️ ⚠️⚠️DevEco Studio 的安装和配置⚠️⚠️ 文章目录 一、打开鸿蒙开发工具官网二、下载 DevEco Studio三、配置 DevEco Studio四、错误处理 ⚠️⚠️⚠️❤️❤️ 关注了解更多 一、打开鸿蒙开发工具官网 下面…

可视化文件编辑与SSH传输神器WinSCP如何公网远程本地服务器

可视化文件编辑与SSH传输神器WinSCP如何公网远程本地服务器 文章目录 可视化文件编辑与SSH传输神器WinSCP如何公网远程本地服务器1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 …

美团2023年Q3财报:营收765亿元 即时零售订单量增至62亿笔

11月28日&#xff0c;美团(股票代码:3690.HK)发布2023年第三季度业绩&#xff0c;公司当季收入765亿元(人民币&#xff0c;下同)&#xff0c;较去年同比增长22.1%。基于提质增效的经营策略&#xff0c;主体业务表现稳固健康&#xff0c;带动公司整体经调整净利润为57.3亿元。 …

VAE模型及pytorch实现

VAE模型及pytorch实现 VAE模型推导部分最小化KL散度推导代码部分损失函数Encoder部分Decoder部分VAE整体架构 VAE问题参考资料 VAE&#xff08;变分自编码器&#xff09;是一种生成模型&#xff0c;结合了自编码器和概率图模型的思想。它通过学习数据的潜在分布&#xff0c;可以…

【数据库】基于排序算法的去重,集合与包的并,差,交,连接操作实现原理,执行代价以及优化

基于两趟排序的其它操作 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏…

跨标签页通信的8种方式(下)

跨标签页通信是指在浏览器中的不同标签页之间进行数据传递和通信的过程。在传统的Web开发中&#xff0c;每个标签页都是相互独立的&#xff0c;无法直接共享数据。然而&#xff0c;有时候我们需要在不同的标签页之间进行数据共享或者实现一些协同操作&#xff0c;这就需要使用跨…

C语言——字符函数和字符串函数(上)

在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了方便操作字符和字符串&#xff0c;C语⾔标准库中提供了⼀系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 一、 字符分类函数 C语⾔中有⼀系列的函数是专⻔做字符分类的&#xff0c;也就是⼀个字…

AI Agent应用落地前半场,属于企服软件厂商推出的平台级AI智能体

GPTs大受欢迎但问题多&#xff0c;企服厂商的AI Agent更被B端客户器重 比尔盖茨预言智能体是下个平台&#xff0c;超自动化平台的AI Agent更靠谱&#xff1f; 以GPTs为代表的AI Agent只是玩具&#xff1f;揭秘真实可用AI智能体长什么样 AI Agent应用落地前半场&#xff0c;属…

mysql处理40w数据脚本执行慢问题

需求背景&#xff1a; 2张表 SS_ZYXX 1w数据&#xff0c;WD_GZPZ 50w数据 SS_ZYXX.id WD_GZPZ.zyxx_id 找到SS_ZYXX表有数据&#xff0c;关联表WD_GZPZ没有数据的SS_ZYXX表的id 处理方案 方案一&#xff1a; 联合查询&#xff1a; 下面sql&#xff0c;在mysql执行时间3…

基于Python的网络爬虫设计与实现

基于Python的网络爬虫设计与实现 摘要&#xff1a;从互联网时代开始&#xff0c;网络搜索引擎就变得越发重要。大数据时代&#xff0c;一般的网络搜索引擎不能满足用户的具体需求&#xff0c;人们更加注重特定信息的搜索效率&#xff0c;网络爬虫技术应运而生。本设计先对指定…

[栈迁移+ret滑梯]gyctf_2020_borrowstack

题目来源buuctf——gyctf_2020_borrowstack 参考链接https://www.shawroot.cc/2097.html 题目信息ubuntu16、64位 第一个read仅溢出一个机器字长&#xff0c;需要栈迁移 解题步骤栈偏移到全局变量bank中&#xff0c;ret2libcgadget 关键步骤 ret滑梯 第二个payload需要添…

内网隧道学习

默认密码&#xff1a;hongrisec2019 一.环境搭建 网卡学习 一个网卡一个分段&#xff0c;想象成一个管道 192.168.52一段 192.168.150一段 仅主机模式保证不予外界连通&#xff0c;保证恶意操作不会跑到真实机之上 52段是内部通信&#xff0c;150段属于服务器&#xff08;…

深入理解强化学习——马尔可夫决策过程:备份图(Backup Diagram)

分类目录&#xff1a;《深入理解强化学习》总目录 在本文中&#xff0c;我们将介绍备份&#xff08;Backup&#xff09;的概念。备份类似于自举之间的迭代关系&#xff0c;对于某一个状态&#xff0c;它的当前价值是与它的未来价值线性相关的。 我们将与下图类似的图称为备份图…

sping boot的配置文件application.properties乱码

1.问题描述 查看spingboot项目中的配置信息&#xff0c;即查看application.properties文件&#xff0c;它出现乱码 2.解决问题 File->Settings->Editor->File Encodings 3.验证是否解决乱码

三维gis中用纹理限定多边形地理区域

在三维 gis 中经常需要在指定的多边形地理范围内做一些操作&#xff0c;比如地形的多边形裁剪、压平多边形区域内的倾斜摄影模型、在指定地理范围内绘制等间距的点等。这都涉及到限定多边形区域的问题。 所谓的限定多边形地理区域&#xff0c;核心问题在于判断某个片元是否处于…

如何获取高质量的静态住宅IP代理?常见误区与注意事项

静态住宅IP代理在今天的网络营销领域扮演着至关重要的角色&#xff0c;静态住宅IP代理以其稳定性和高匿名性&#xff0c;为互联网业务提供了一个安全的执行环境。通过模拟真实用户的网络行为&#xff0c;这些IP代理降低了企业在网络营销活动中被识别和封禁的风险。它保护了企业…

this.$refs,salesRankRefjj.searchRankCall is not a function

在vue项目中&#xff0c;在父组件使用$refs获取不到子组件的方法&#xff0c;为什么&#xff1f; 我的报错如下&#xff1a; [Vue wamn]: Error in v-on handler: "TypeError: this.$refs,salesRankRefjj.searchRankCall is not a function found in 代码如下&#xff1a…

Ultipa参加国际科学会议KGSWC2023

近日&#xff0c;领先的国际科学会议 KGSWC 2023&#xff0c;在西班牙萨拉戈萨大学召开&#xff0c;Ultipa Graph参加。 KGSWC2023是第五届伊比利亚-美洲会议和第四届印度-美洲知识图谱与语义网大会的联合论坛。自2019年成立以来&#xff0c;KGSWC一直是一个重要的学术活动&am…

陶陶摘苹果、跳跃游戏

1. 陶陶摘苹果 题目描述&#xff1a; 陶陶家的院子里有一棵苹果树&#xff0c;每到秋天树上就会结出 10 个苹果。苹果成熟的时候&#xff0c;陶陶就会跑去摘苹果。陶陶有个 30 厘米高的板凳&#xff0c;当她不能直接用手摘到苹果的时候&#xff0c;就会踩到板凳上再试试。 现在…