aiohttp WebSocket 关闭码校验修复:拒绝对端 Close 帧中的 1006(ABNORMAL_CLOSURE) 后端Web框架WebSocket【免费下载链接】aiohttpAsynchronous HTTP client/server framework for asyncio and Python项目地址https://gitcode.com/gh_mirrors/ai/aiohttp点击查看免费下载导读本文围绕 aiohttp 的 bugfix 变更CHANGES/13536.bugfix.rst展开WebSocket Reader 此前会错误地接受对端 Close 帧中携带的关闭码1006而根据 RFC 64551006属于保留码禁止出现在网络传输的 Close 帧中。文章将结合仓库源码与测试用例剖析该问题的成因、修复后的校验逻辑、Python/Cython 双实现路径以及1006在 aiohttp 内部作为本地状态码WSCloseCode.ABNORMAL_CLOSURE的正当用法帮助读者理解 WebSocket 协议合规性校验的底层实现。一、问题背景RFC 6455 中的关闭码语义WebSocket 协议RFC 6455定义了 Close 控制帧用于在连接两端主动终止会话时传递一个 2 字节的关闭状态码close code和可选的文本原因。关闭码被划分为几个区间1000–2999协议保留区间其中1000–1015是固定语义的预定义码3000–3999留给库、框架和应用自行注册使用4000–4999供私有应用使用。其中1006ABNORMAL CLOSURE是一个特殊的存在它被 RFC 6455 第 7.4.1 节定义为连接在缺少 Close 帧的情况下异常关闭的语义仅供本地实现内部标记使用绝不能作为帧内容在网络上发送。同理1004保留、1005无状态码、1015TLS 握手失败也都是禁止上线的保留码。aiohttp 在 aiohttp/_websocket/models.py 中通过WSCloseCode枚举完整登记了这些预定义码class WSCloseCode(IntEnum): OK 1000 GOING_AWAY 1001 PROTOCOL_ERROR 1002 UNSUPPORTED_DATA 1003 ABNORMAL_CLOSURE 1006 INVALID_TEXT 1007 POLICY_VIOLATION 1008 MESSAGE_TOO_BIG 1009 MANDATORY_EXTENSION 1010 INTERNAL_ERROR 1011 SERVICE_RESTART 1012 TRY_AGAIN_LATER 1013 BAD_GATEWAY 1014注意ABNORMAL_CLOSURE 1006是枚举成员之一aiohttp 在本地用它标记连接异常中断但这不意味着它可以被写入 Close 帧对外发送——这正是本次 bugfix 修复的边界问题。二、问题本身Reader 误接受对端下发的 1006在修复之前WebSocket Reader 在解析 Close 帧时对关闭码的合法性检查存在漏洞导致对端peer在 Close 帧中携带1006时会被当作合法值接受而不是按协议要求以1002 PROTOCOL_ERROR拒绝。这个问题的严重性在于违反 RFC 6455 的显式规定1006不应出现在线上帧中在 aiohttp 内部1006有专门含义连接异常关闭若被当作对端显式关闭码透传给上层应用会造成语义混淆——应用层会分不清对端发来了 1006与本地检测到连接异常协议解析器作为安全边界接受非法状态码可能成为畸形/不规范实现混入的入口。三、修复方案ALLOWED_CLOSE_CODES 白名单校验修复的核心在 aiohttp/_websocket/reader_py.py。源码通过构造一个允许上线的关闭码集合显式把1006排除在外# ABNORMAL_CLOSURE is used internally, should never be accepted from a client. # https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1 ALLOWED_CLOSE_CODES {int(i) for i in WSCloseCode} - { int(WSCloseCode.ABNORMAL_CLOSURE) }这一行注释直接点明了本次修复的设计意图ABNORMAL_CLOSURE仅供内部使用绝不应从对端接受。随后的 Close 帧处理逻辑reader_py.py在解出 2 字节关闭码后执行完整的合法性判定elif opcode OP_CODE_CLOSE: payload_len len(payload) if payload_len 2: close_code UNPACK_CLOSE_CODE(payload[:2])[0] # https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.2 if close_code 4999 or ( close_code 3000 and close_code not in ALLOWED_CLOSE_CODES ): raise WebSocketError( WSCloseCode.PROTOCOL_ERROR, fInvalid close code: {close_code}, ) ...校验规则可以拆解为两条条件判定结果close_code 4999含5000–65535超出合法区间报1002 PROTOCOL_ERRORclose_code 3000且不在ALLOWED_CLOSE_CODES中属于保留/禁止码如1004、1005、1006、1015报1002 PROTOCOL_ERROR由于ALLOWED_CLOSE_CODES是从WSCloseCode枚举全集剔除1006后得到的因此1006必然落入第二条分支被拒绝。修复后任何携带1006的 Close 帧都会触发WebSocketErrorcode1002该异常会经由feed_data的异常捕获路径reader_py.py写入队列最终以上层可见的协议错误形式暴露给调用方。此外Close 帧还有其他合规性校验payload 长度必须为 0 或 ≥21 字节的 payload 会被判定为非法见 reader_py.pypayload 中的文本部分必须是合法 UTF-8否则报1007 INVALID_TEXT。四、Python 与 Cython 双实现修复如何覆盖两条代码路径aiohttp 的 WebSocket 解析器存在双实现架构纯 Python 实现aiohttp/_websocket/reader_py.pyCython 加速实现aiohttp/_websocket/reader_c.pxd 定义了WebSocketReader/WebSocketDataQueue的 C 级接口模块入口 aiohttp/_websocket/reader.py 负责按运行环境选择实现当NO_EXTENSIONS为真或 Cython 扩展导入失败时回退到 Python 版本否则优先加载编译后的reader_c版本。ALLOWED_CLOSE_CODES这个集合在 reader_c.pxd 中被声明为cdef set与 Python 实现共享同一份语义定义——因此本次校验修复对两条代码路径同时生效无论用户安装的是纯 Python 包还是带 Cython 扩展的预编译 wheel行为一致。从实现细节看_handle_frame的 close 分支同样在 Cython 版本中通过cpdef暴露reader_c.pxd关闭码的解析借助UNPACK_CLOSE_CODE字节解包辅助函数完成确保 Python 与 C 路径的校验结果严格等价。五、测试验证保留码参数化用例仓库测试 tests/test_websocket_parser.py 为本次修复提供了直接的回归保护pytest.mark.parametrize(code, (1004, 1005, 1006, 1015)) def test_close_frame_reserved_code(parser: PatchableWebSocketReader, code: int) - None: # https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1 # 1004, 1005, 1006 and 1015 are resreved and forbidden as a # status code in a Close frame on the wire. 1006 is a WSCloseCode member # (aiohttp uses it locally), so it must still be rejected on receipt. data build_close_frame(codecode) with pytest.raises(WebSocketError) as ctx: parser._feed_data(data) assert ctx.value.code WSCloseCode.PROTOCOL_ERROR该测试的注释与源码注释相互印证1006虽然是WSCloseCode枚举成员本地使用但在接收时必须拒绝。测试将1004、1005、1006、1015四个保留码统一参数化确保它们全部被解析器以PROTOCOL_ERROR拒绝。同一测试文件中还有配套的边界用例test_websocket_parser.py覆盖5000、9999、65535等超出4999上限的非法关闭码与白名单逻辑形成完整闭环。六、1006 在 aiohttp 内部的正当用法ABNORMAL_CLOSURE修复并没有移除1006的枚举定义——它在 aiohttp 内部依然承担重要职责只是使用场景被严格限定在本地状态标记而非线上帧内容。在功能测试中可以看到WSCloseCode.ABNORMAL_CLOSURE被用来断言连接异常关闭的结果tests/test_client_ws_functional.py 等多处断言resp.close_code is WSCloseCode.ABNORMAL_CLOSURE即客户端在连接被异常中断未收到 Close 帧时本地记录关闭码为1006tests/test_web_websocket_functional.py 等服务端测试同样用ws.close_code WSCloseCode.ABNORMAL_CLOSURE断言异常断开场景。也就是说1006在 aiohttp 的语义是对端没有发送 Close 帧就断开了连接——这是本地检测到的异常状态close_code属性会以ABNORMAL_CLOSURE呈现给应用层。这与从线上帧中解析出 1006是完全不同的两条路径。本次 bugfix 的意义正在于堵住后一条路径既然1006是本地状态的专属标记那么任何从对端帧中宣称1006的行为都必然是非法的协议数据必须被解析器以1002 PROTOCOL_ERROR拒绝而不能混入本地状态标记中。七、对使用者的影响与升级建议行为变化升级到包含该修复的版本后如果对端客户端或服务端在 Close 帧中携带1006aiohttp 不再将其透传为关闭码而是抛出WebSocketErrorcode1002并触发连接关闭。这符合 RFC 6455 对保留码的规定属于协议合规性收紧。自查对端实现若你的应用需要与某些不规范实现的 WebSocket 服务端/客户端互通请确认对端不会在 Close 帧中发送1006或1004、1005、1015。规范的做法是仅在本地用1006标记连接异常线路上永远不发送它。应用层判断逻辑不变ws.close_code仍会在连接异常中断时返回WSCloseCode.ABNORMAL_CLOSURE本地语义这部分行为未受影响受影响的是对端帧中显式携带 1006这一非法输入的处理。关注测试入口如需验证自己的构建版本是否包含该修复可直接运行 tests/test_websocket_parser.py 中的test_close_frame_reserved_code参数化用例观察1006是否被拒绝。小结本次 bugfix 以一段精巧的白名单集合ALLOWED_CLOSE_CODES WSCloseCode 全集 - {1006}修复了 WebSocket Reader 对保留关闭码的校验漏洞使 aiohttp 在拒绝非法关闭码与本地使用 1006 标记异常关闭两条语义之间划清了边界。从 models.py 的枚举定义、reader_py.py 的校验实现到 reader_c.pxd 的 Cython 双路径覆盖再到 test_websocket_parser.py 的回归测试仓库中形成了完整一致的协议合规性闭环——这正是 aiohttp 作为 asyncio 生态 WebSocket 实现所应具备的严谨性。赞分享后端Web框架WebSocket【免费下载链接】aiohttpAsynchronous HTTP client/server framework for asyncio and Python项目地址https://gitcode.com/gh_mirrors/ai/aiohttp点击查看免费下载相关推荐aiohttp WebSocket CLOSE 帧状态码校验按 RFC 6455 拒绝越界关闭码4999的协议错误处理aiohttp WebSocket CLOSE 帧状态码校验按 RFC 6455 拒绝越界关闭码 4999的协议错误处理 导读 本文围绕 aiohttp后端Web框架WebSocketaiohttp WebSocket 分片消息协议校验修复严格按 RFC 6455 拒绝分片间注入的数据帧aiohttp WebSocket 分片消息协议校验修复严格按 RFC 6455 拒绝分片间注入的数据帧 本篇技术指南围绕 aiohttp 的 WebSock后端Web框架WebSocketaiohttp WebSocket 大消息整型溢出漏洞修复解析从 max_msg_size 校验到 1009 关闭码aiohttp WebSocket 大消息整型溢出漏洞修复解析从 max_msg_size 校验到 1009 关闭码 导读 本文基于 aiohttp 官方变更后端Web框架WebSocket上一篇Unlock Music 使用指南3 步在浏览器中解锁加密音乐无需安装下一篇craft-agents-oss 0.2.33 版本技术解析OAuth 认证统一、~/.claude.json 自动修复与稳定性加固创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考