【Bug已解决】[Security] Path traversal + malicious-model DoS via sharded-checkpoint `weight_map` 解决方案
【Bug已解决】[Security] Path traversal malicious-model DoS via sharded-checkpointweight_map解决方案一、现象长什么样加载一个分片 checkpointsafetensors / 多个 shard 文件 一个索引文件索引里有weight_map把张量名映射到分片文件名时如果索引文件被篡改或来自不可信来源可能出现两类危险路径穿越Path Traversalweight_map里某条写成{lm_head.weight: ../../../etc/secrets}或绝对路径/etc/passwd加载器照着去读/写checkpoint 目录之外的文件可能泄露宿主机文件或覆盖关键文件。恶意模型 DoSweight_map引用一个不存在的巨文件或循环引用加载器卡死在等待/扫描或一次性尝试分配超大内存 → 进程挂起 / OOM。报错可能是FileNotFoundError: ../outside/model.safetensors (企图读目录外) # 或更糟静默读了宿主机文件毫无报错或更隐蔽的「加载卡死十几分钟」DoS。本质checkpoint 索引里的weight_map文件名是被直接当作文件路径使用的但加载器没有校验这些文件名是否「安全地落在 checkpoint 目录内」。不可信 checkpoint 借此实现路径穿越或 DoS。二、背景分片 checkpoint 的结构model.safetensors.index.json索引文件含metadata和weight_map例如{ weight_map: { model.embed_tokens.weight: model-00001-of-00003.safetensors, lm_head.weight: model-00003-of-00003.safetensors } }一堆model-0000X-of-0000Y.safetensors分片文件。加载流程读索引 → 对每个张量按weight_map[name]拿到分片文件名 → 打开checkpoint_dir / 文件名读取。安全性问题就在这个「打开checkpoint_dir / 文件名」如果weight_map[name]是../evil.safetensorscheckpoint_dir / ../evil.safetensors解析后跳出目录 → 读/写目录外。如果是绝对路径/etc/passwd更是直接越界。如果引用一个 100GB 但实际不存在 / 符号链接到/dev/random的文件加载器会卡死或崩。这类问题在「加载用户上传的 / 从 HF hub 拉的不可信 checkpoint」场景尤其危险。防御性做法永远不要把weight_map的文件名当裸路径拼必须先归一化、校验落在目录内、拒绝..和绝对路径。三、根因根因是加载器信任weight_map里的文件名、直接拼接路径未做目录边界校验与异常处理三层第一层主因文件名直接拼路径无边界校验。open(checkpoint_dir / weight_map[name])没有先os.path.normpath 校验「结果仍在checkpoint_dir内」。于是../ 绝对路径直接越界。第二层无大小/存在性预检导致 DoS。加载器遇到weight_map引用的文件直接open或mmap不先检查文件是否存在、大小是否合理。恶意 checkpoint 可让加载器去读一个不存在的巨大路径卡死等待或触发超大 mmapOOM。第三层错误信息泄露 / 静默越界。路径穿越时若只是FileNotFoundError攻击者能据此探测宿主机目录结构哪些文件存在若静默成功读取目录外文件则更危险。理想是「统一拒绝 不暴露内部路径」。一句话weight_map文件名被当可信路径直接使用缺目录边界校验与大小/存在预检导致路径穿越与 DoS。四、最小可运行复现下面用纯 Python 模拟「weight_map含..路径加载器未校验即越界」的控制流不需要真实文件import os def resolve_buggy(checkpoint_dir: str, entry: str) - str: # 错误直接拼接不校验 return os.path.join(checkpoint_dir, entry) def load_buggy(checkpoint_dir: str, weight_map: dict): for name, fname in weight_map.items(): path resolve_buggy(checkpoint_dir, fname) print(f将打开: {path}) def main(): ckpt /models/safe_ckpt weight_map { embed.weight: model-00001.safetensors, lm_head.weight: ../../etc/passwd, # 恶意穿越 head.weight: /absolute/etc/shadow, # 绝对路径 } load_buggy(ckpt, weight_map) # 输出里会出现 /etc/passwd、/absolute/etc/shadow —— 越界 if __name__ __main__: main()跑出来会打印出/etc/passwd和/absolute/etc/shadow这类目录外路径——演示了不加校验时weight_map如何被用来路径穿越。五、解决方案第一层最小直接修复最省事的救火加载前对所有weight_map文件名做安全解析拒绝..和绝对路径。用os.path.normpath 边界校验import os def safe_resolve(checkpoint_dir: str, entry: str) - str: # 1) 拒绝绝对路径 if os.path.isabs(entry): raise ValueError(fweight_map 含绝对路径拒绝: {entry}) # 2) 归一化并校验仍落在 checkpoint_dir 内 base os.path.abspath(checkpoint_dir) target os.path.normpath(os.path.join(base, entry)) if not (target base or target.startswith(base os.sep)): raise ValueError(f路径穿越被拒绝: {entry} - {target}) return target def load_safe(checkpoint_dir: str, weight_map: dict, max_bytes: int 50_000_000_000): for name, fname in weight_map.items(): path safe_resolve(checkpoint_dir, fname) # 3) 存在性 大小预检防 DoS if not os.path.exists(path): raise FileNotFoundError(f分片缺失: {path}) if os.path.getsize(path) max_bytes: raise ValueError(f分片过大疑似恶意: {path} {max_bytes} 字节) # 真正打开... print(安全打开:, path)临时用这个函数包裹所有weight_map解析路径穿越和超大文件 DoS 都被挡下。六、解决方案第二层结构性改进第一层是「手动校验」第二层是「把安全解析做成加载器的默认行为且对符号链接、大小、数量都做防御」从设计上消灭信任weight_mapimport os from dataclasses import dataclass from typing import Dict dataclass class SafeLoadPolicy: checkpoint_dir: str max_shard_bytes: int 50_000_000_000 # 单分片上限 50GB max_shards: int 10_000 # 分片数量上限防海量小文件 DoS def resolve(self, entry: str) - str: base os.path.abspath(self.checkpoint_dir) if os.path.isabs(entry): raise ValueError(f拒绝绝对路径: {entry}) target os.path.normpath(os.path.join(base, entry)) # 解析符号链接后再校验一次防止软链逃逸 real_target os.path.realpath(target) if not (real_target base or real_target.startswith(base os.sep)): raise ValueError(f路径穿越被拒绝: {entry}) return real_target def validate_shard(self, path: str) - None: if not os.path.isfile(path): raise FileNotFoundError(f分片不存在: {os.path.basename(path)}) if os.path.getsize(path) self.max_shard_bytes: raise ValueError(f分片超过上限 {self.max_shard_bytes} 字节疑似恶意) # 不暴露内部绝对路径错误信息只用文件名 return None def load(self, weight_map: Dict[str, str]) - None: if len(weight_map) self.max_shards: raise ValueError(f分片数量 {len(weight_map)} 超过上限 {self.max_shards}) seen set() for name, fname in weight_map.items(): path self.resolve(fname) self.validate_shard(path) if path in seen: raise ValueError(f分片重复引用疑似循环: {fname}) seen.add(path) # 真正加载... print(安全加载:, os.path.basename(path))关键防御点解析符号链接realpath后再次校验防软链逃逸。错误信息只用文件名、不暴露宿主机绝对路径防信息泄露。数量上限 单文件大小上限 重复引用检测三重防 DoS。七、解决方案第三层断言 / CI 守护把「拒绝穿越」「拒绝绝对路径」「大小/数量上限」「不泄露路径」固化成测试import pytest def test_reject_dotdot(): p SafeLoadPolicy(checkpoint_dir/models/ckpt) with pytest.raises(ValueError): p.resolve(../../etc/passwd) def test_reject_absolute(): p SafeLoadPolicy(checkpoint_dir/models/ckpt) with pytest.raises(ValueError): p.resolve(/etc/shadow) def test_accept_relative_safe(): p SafeLoadPolicy(checkpoint_dir/models/ckpt) out p.resolve(model-00001.safetensors) assert out.startswith(os.path.abspath(/models/ckpt)) def test_reject_oversized_shard(tmp_path): big tmp_path / big.safetensors big.write_bytes(bx * 100) p SafeLoadPolicy(checkpoint_dirstr(tmp_path), max_shard_bytes10) with pytest.raises(ValueError): p.validate_shard(str(big)) def test_reject_too_many_shards(): p SafeLoadPolicy(checkpoint_dir/x, max_shards2) with pytest.raises(ValueError): p.load({fw{i}: fs{i} for i in range(5)}) def test_no_internal_path_leak(): p SafeLoadPolicy(checkpoint_dir/models/ckpt) try: p.resolve(../../secret) except ValueError as e: # 错误信息不应暴露宿主机真实绝对路径细节 assert /models/ckpt not in str(e) or 拒绝 in str(e)再加一个端到端回归加载含恶意weight_map的不可信 checkpoint 时被拒def test_untrusted_checkpoint_blocked(tmp_path): index {weight_map: {w: ../../etc/passwd}} write_index(tmp_path / index.json, index) p SafeLoadPolicy(checkpoint_dirstr(tmp_path)) with pytest.raises(ValueError): p.load(index[weight_map])八、排查清单看加载不可信 checkpoint 时是否出现目录外路径、卡死或 OOM → 是路径穿越/DoS。检查加载器是否直接os.path.join(ckpt_dir, weight_map[name])而无校验。临时救火用safe_resolve包裹所有weight_map解析拒绝../绝对路径 大小预检。确认符号链接是否被利用realpath后再校验一次。长期修复安全解析做成加载器默认行为含软链校验、大小/数量上限、信息不泄露。升级 accelerate/transformers 到合了该安全解析的版本并跑上面的「拒绝穿越」用例。仅加载来自可信源的 checkpoint不可信模型先离线扫描weight_map再决定。九、小结分片 checkpoint 的weight_map路径穿越与 DoS不是加载器功能错了而是它信任索引里的文件名、直接拼接路径未做目录边界校验与大小/存在预检不可信 checkpoint 借此越界或卡死。最小修复是safe_resolve拒绝../绝对路径 大小预检结构性修复是把安全解析做成默认行为含符号链接二次校验、大小/数量上限、错误不泄露内部路径最后用 pytest 把「拒绝穿越」「拒绝绝对路径」「上限防御」锁死。抓住「任何来自外部的配置里的路径都必须归一化后校验仍在基目录内、绝不信任」这条安全铁律所有加载不可信文件的场景都应照此设防。

相关新闻

Minecraft红石系统核心原理与自动化应用全解析

Minecraft红石系统核心原理与自动化应用全解析

1. 红石系统:从入门到精通的基石如果你在Minecraft里还停留在用镐子挖矿、用剑砍怪的阶段,那你就错过了这个游戏一半以上的乐趣。红石,这个游戏内最接近“电”与“逻辑”的魔法系统,是区分普通玩家和进阶工程师的分水岭。它能让你…

2026/9/25 3:35:09 阅读更多 →
2024年LaTeX入门指南:从TeX Live安装到第一个文档编译

2024年LaTeX入门指南:从TeX Live安装到第一个文档编译

1. 项目概述:为什么在2024年,我依然推荐你从LaTeX开始如果你经常需要和公式、论文、书籍排版打交道,或者你受够了Word在调整格式时那种“牵一发而动全身”的无力感,那么LaTeX这个名字你应该不陌生。它不是一个软件,而是…

2026/9/25 7:37:36 阅读更多 →
浅谈“随机按键指定串”问题

浅谈“随机按键指定串”问题

Preface 主要作为培训时该类问题的总结。 Introduction 这类问题的主要形式是: 有 mmm 个不同的字符按键,进行 nnn 次(或无限次)随机敲打。 询问 nnn 个字符中出现长度为 kkk 的指定串 SSS 的概率。 或求无限次敲打中 SSS 出现…

2026/9/19 4:26:26 阅读更多 →

最新新闻

ESP32开发板更换后为何需重新适配?小智源码板级适配全解析

ESP32开发板更换后为何需重新适配?小智源码板级适配全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:37:56 阅读更多 →
奈氏图完全解析:从传递函数到闭环稳定性判据

奈氏图完全解析:从传递函数到闭环稳定性判据

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:37:56 阅读更多 →
DeepSeek-V4 预览版百万上下文实战:TaoToken 统一 Key 接入与 config.toml 配置骨架

DeepSeek-V4 预览版百万上下文实战:TaoToken 统一 Key 接入与 config.toml 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:37:56 阅读更多 →
Win10远程桌面开启失败的五层逻辑链解析

Win10远程桌面开启失败的五层逻辑链解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:37:56 阅读更多 →
小米手机USB调试安装必须插SIM卡?原理与绕过方案

小米手机USB调试安装必须插SIM卡?原理与绕过方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:37:56 阅读更多 →
ESP32上跑WebAssembly:为何不能直接操作硬件?Host API桥接才是正解

ESP32上跑WebAssembly:为何不能直接操作硬件?Host API桥接才是正解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 7:36:56 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →