路径没有越界,读取为何仍会挂起?Werkzeug 与 Windows 文件名语义

背景与时间线:新收录不等于新攻击

本次选题的时效依据是 GitHub 已审核数据库在近 72 小时的收录。项目此前已披露相关问题,不能把数据库日期当作攻击发生日期。以下事实以安全公告及其关联的项目发布资料为依据;后续工程分析和建议不代表厂商确认的事故结论。

核验项结果
漏洞编号CVE-2026-102598
项目公告日期2026-09-27
GitHub 已审核数据库收录日期2026-10-05
受影响与最低修复边界Werkzeug ❤️.1.9;最低修复版本 3.1.9;本问题涉及 Windows 与 NTFS 条件
攻击发生时间所引用资料未提供可确认的现实攻击时间

技术原理与信任边界

官方公告确认 safe_join 对带空 ADS 标记的 Windows 特殊设备名处理不完整;使用 send_from_directory 的相关 Windows/NTFS 应用可能在读取时挂起。3.1.9 发布说明列出该修复,PR #3309对应名称过滤更新。

这是文件名语义问题,不能直接描述成目录穿越、任意文件读取或 RCE。即使规范化后的字符串仍位于指定目录,操作系统也未必把末尾组件当作常规文件。路径边界、对象类型和读取预算是三个不同问题。

工程分析:跨平台服务不宜让业务 ID 直接成为任意路径。更稳妥的设计是将不透明文件 ID 映射到服务端生成的名字,再单独记录用户展示名称。这样能明显减少必须解释的路径语法,但不能替代底层文件打开时的对象检查和权限控制。

影响范围:从依赖存在到路径可达

依赖清单命中是调查入口,还需要确认相关功能是否启用、输入能否被不可信主体影响、程序以何种权限运行,以及产物或状态是否跨任务复用。本文不把潜在后果写成已经发生的事故,也不以公开说明等同于在野利用证据。

建议保留一份服务级判定记录:运行版本、调用点、输入来源、权限边界和处置负责人。由开发确认调用路径,安全团队确认不可信输入如何进入,平台团队确认补丁是否覆盖运行实例。这比单纯将漏洞工单标记为“依赖已升级”更有验证价值。

防御性实验:只验证安全不变量

下列 Python 3 示例仅处理本地内存数据,不攻击真实服务,可独立运行。

import re

def allowed_component(name):
    # 教学模型:主动限制为保守的 ASCII 文件名子集。
    if not re.fullmatch(r"[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*", name):
        return False
    base = name.split('.', 1)[0].upper()
    reserved = {'CON', 'PRN', 'AUX', 'NUL'}
    reserved.update('COM' + str(i) for i in range(1, 10))
    reserved.update('LPT' + str(i) for i in range(1, 10))
    return base not in reserved

assert allowed_component('report-2026.txt')
assert not allowed_component('NUL')
assert not allowed_component('CON.txt')
assert not allowed_component('report:')
assert not allowed_component('../report')
print("5 项纯字符串检查通过")

示例不会打开任何路径;其规则比一般文件系统更严格,不支持 Unicode 文件名、路径分隔符和 ADS。它用于说明可接受子集策略,不是 Werkzeug 补丁的完整重写。若业务要支持更丰富的文件名,应使用修复后的库与操作系统语义测试,而非随意扩大这段正则。

测试还应包括合法输入,确保防护不是通过关闭全部功能而获得表面成功。负向用例要断言拒绝发生的位置及副作用,而不只是断言最终返回错误。若测试仅验证“抛出了异常”,异常前已经发生的网络连接、内存分配或代码生成仍可能被漏掉。

修复与防护建议

盘点 Windows 上实际提供文件下载的服务及 Werkzeug 运行版本,升级到包含修复的版本。Linux 部署不能据此被判为本漏洞可利用,但共享代码库仍应保留 Windows CI 回归任务。

把路径测试分成目录边界、名称语义、符号链接、普通文件类型和取消清理五组。测试特殊名称时优先验证拒绝结果,避免打开设备。对下载设置超时和工作进程隔离,并确认客户端断开后服务端读取能结束。仅在反向代理上设置超时,可能只关闭连接而没有释放后端线程。

交付验收清单

责任方应交付的证据
研发相关调用路径说明、合法与拒绝用例、版本升级差异
安全输入信任边界、历史暴露判断、剩余风险记录
平台实际运行版本、部署范围、失败回滚与监控方案

修复关闭当前缺陷后,仍应把相同类型的边界检查纳入后续开发约束。没有异常日志可能意味着未发生攻击,也可能意味着没有记录到关键阶段;应结合暴露条件解释证据,避免作出超出可见范围的结论。

总结

本案例的实用价值,是把依赖告警转化成可验证的工程问题:哪个输入获得了超出预期的解释,哪一步必须先执行校验,以及升级后哪些运行对象需要重新确认。版本修复、调用链审查和回归测试应共同构成关闭风险的依据。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐