Skip to content

[bug] GBK 编码响应头解析错误 #2405

@icremankozleesah-cloud

Description

@icremankozleesah-cloud

Reqable Bug Report: GBK 编码响应头解析错误

问题描述

Reqable 在处理包含中文 GBK 编码的 HTTP 响应头(特别是 Content-Disposition 头)时,存在字符编码解析错误,导致中文文件名显示为乱码或问号。

问题复现

测试环境

  • 目标域名: xx.com.cn(隐私)
  • 文件类型: PDF 研报下载
  • 问题场景: 服务端返回的 Content-Disposition 响应头包含 GBK 编码的中文文件名

Image

实际案例

案例 1

服务端发送的响应头(推测,基于常见服务端实现):

Content-Disposition: attachment; filename="20260205-华泰证券-2026年游戏行业发展展望.pdf"

其中 "华泰证券" 等中文以 GBK 编码存储在 HTTP 头中。

Reqable 解析后的结果(在插件中通过 response.headers['content-disposition'] 获取):

attachment; filename="20260205-????֤ȯ-2026??Ϸ??ҵ????չ??????to B??to C??AI??Ϸ???濪ʼ????ƽ̨????.pdf"

字节级分析:

# 部分字符的码点值(通过 ord() 获取)
'?'0x3f       # 问号(解码失败标志)
'֤'0x5a4      # 应该是两个 GBK 字节被错误合并
'ȯ'0x22f      # 应该是两个 GBK 字节被错误合并  
'Ϸ'0x3f7      # 应该是两个 GBK 字节被错误合并

从字节码可以看出,Reqable 将 GBK 双字节序列错误地解析成了 Unicode 字符。

案例 2

Raw: 'attachment; filename="20260201-??????-??۾??ú?ծȯ?г?һ?ܹ۵㣺????????ծ????519ֻ??.pdf"'
字节码: [..., '0x6fe', '0x56e', '0x22f', '0x433', '0x79', '0x68', ...]

包含大量问号 ? 和超过 0xFF 的 Unicode 字符(如 0x6fe, 0x56e),说明解码彻底失败。

问题根因分析

HTTP 协议规范

根据 RFC 2616RFC 5987

  • HTTP 响应头默认应使用 ISO-8859-1 (Latin-1) 编码
  • 对于非 ASCII 字符(如中文),应使用 RFC 2231/5987 扩展格式:
    Content-Disposition: attachment; filename*=UTF-8''%E4%B8%AD%E6%96%87.pdf
    
  • 或者使用百分号编码

服务端实现问题

xx.com.cn 服务端没有遵循 RFC 规范,而是:

  1. 直接将 GBK 编码的字节流放入 filename="..."
  2. 没有使用 filename*= 扩展格式
  3. 没有进行百分号编码

虽然服务端不规范,但许多 HTTP 客户端(如浏览器)能够正确处理这种情况,因为它们会:

  • 尝试检测非 ASCII 字节
  • 根据系统或响应内容推测编码(GBK/GB18030/UTF-8)
  • 自动转码为正确的 Unicode

Reqable 的问题

Reqable 在解析响应头时,似乎使用了固定的 UTF-8 解码或其他不兼容 GBK 的方式,导致:

  1. 问号 ? 大量出现: GBK 单字节无法用 UTF-8 解码时被替换为 ?
  2. 高位 Unicode 字符: GBK 双字节 (B1, B2) 被错误地合并为一个 Unicode 码点 (B1 << 8 | B2)
    • 例如: GBK 0xBB 0xAA ("华") → 被解析为 Unicode U+BBAA(一个无效字符)

实际字节流对比:

正确的GBK字节 Reqable解析结果 说明
0xBB 0xAA ("华") U+05A4? 错误合并或解码失败
0xCC 0xA9 ("泰") U+052A? 错误合并或解码失败
0xD6 0xA4 ("证") U+05A4 错误合并
0xC8 0xAF ("券") U+022F 错误合并

影响范围

  1. 中国大陆的企业级服务: 许多老旧系统仍使用 GBK 编码
  2. 金融、政务、学术机构: 研报、文档下载场景普遍存在
  3. 用户体验: 下载的文件名显示为乱码,无法识别文件内容

复现步骤

  1. 启动 Reqable 并启用抓包
  2. 访问 https://xxx.com.cn(需要登录)
  3. 下载任意中文名称的 PDF 研报
  4. 观察响应头中的 Content-Disposition 字段
  5. 在插件脚本中通过 response.headers['content-disposition'] 获取,会发现中文乱码

期望行为

Reqable 应该:

方案 1: 智能编码检测(推荐)

  • 在解析响应头时,检测是否包含非 ASCII 字节(>= 0x80
  • 尝试按以下优先级解码:
    1. UTF-8
    2. GBK / GB18030(针对中国用户)
    3. Big5(针对繁体中文)
    4. ISO-8859-1(后备)
  • 选择解码成功且包含有效字符最多的结果

方案 2: 用户配置

  • 在 Reqable 设置中添加"响应头编码"选项
  • 允许用户手动选择: UTF-8 / GBK / GB18030 / ISO-8859-1 / Auto

方案 3: 保留原始字节

  • 在插件 API 中提供访问原始字节的接口:
    # 新增 API
    raw_bytes = response.headers.get_raw_bytes('content-disposition')
    # 插件自行解码
    decoded = raw_bytes.decode('gbk', errors='ignore')

临时解决方案(脚本层面)

由于 Reqable 已经做了错误的字符串解码,插件脚本无法获取原始字节,导致无法在脚本层面完美修复此问题

我们尝试了以下方法但均失败:

  1. latin-1 → GBK 转码(原始字节已丢失)
  2. ✗ 拆分高位 Unicode 字符(拆分后的字节无法正确重组为 GBK)
  3. ✗ 遍历多种编码尝试解码(所有方案都产生大量问号和乱码)

优先级

高优先级 - 影响中国用户日常使用,特别是企业用户和开发者。

相关标准

  • RFC 2616 - HTTP/1.1
  • RFC 5987 - 字符集和语言编码用于超文本传输协议(HTTP)头字段参数
  • RFC 6266 - 在超文本传输协议(HTTP)中使用Content-Disposition头字段

测试用例

建议添加以下测试用例:

# 测试用例 1: GBK 编码的中文文件名
assert parse_header('attachment; filename="测试文件.pdf"', encoding='gbk') == '测试文件.pdf'

# 测试用例 2: UTF-8 编码的中文文件名
assert parse_header('attachment; filename="测试文件.pdf"', encoding='utf-8') == '测试文件.pdf'

# 测试用例 3: RFC 5987 标准格式
assert parse_header('attachment; filename*=UTF-8\'\'%E6%B5%8B%E8%AF%95.pdf') == '测试.pdf'

# 测试用例 4: 混合编码(服务端非标准实现)
assert parse_header('attachment; filename="20260205-华泰证券-报告.pdf"', encoding='gbk') == '20260205-华泰证券-报告.pdf'

联系信息

如需更多信息或测试样本,请联系:


附录: 调试日志示例

[DEBUG] Raw Content-Disposition: 'attachment; filename="20260205-????֤ȯ-2026年游戏行业发展展望.pdf"'
[DEBUG] Content-Disposition bytes: ['0x3f', '0x3f', '0x3f', '0x3f', '0x5a4', '0x22f', ...]
[DEBUG] 字符 '֤' (U+05A4) 应该对应 GBK 字节 0xD6 0xA4 ("证")
[DEBUG] 字符 'ȯ' (U+022F) 应该对应 GBK 字节 0xC8 0xAF ("券")

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions