Ammonia安全史血泪教训:mXSS与DoS漏洞完整清单及版本选型指南
【免费下载链接】ammoniaRepair and secure untrusted HTML项目地址: https://gitcode.com/gh_mirrors/am/ammonia
Ammonia 是一款 Rust 编写的白名单式 HTML 清洗(sanitization)库,专门用于在展示用户提交的不可信 HTML 之前,防范 XSS、页面布局破坏和点击劫持。它基于 Servo 浏览器的 html5ever 解析器,严格按照 HTML5 规范解析输入,因此对语法混淆极具抵抗力。但从它的变更日志看,Ammonia 也曾在命名空间切换、深层嵌套输入等场景下被挖出过 mXSS(突变型 XSS)与 DoS(拒绝服务)漏洞。本文梳理其完整漏洞清单,并给出可直接落地的版本选型建议。
一、30秒认识 Ammonia:Rust HTML 清洗器为什么值得关注
🧹 Ammonia 的核心工作流只有三步:构建 DOM → 按白名单遍历过滤 → 重新序列化。
- 白名单机制:默认只放行一小撮安全的标签与属性,
script、style等标签内容会被整段剥离 - 与浏览器同款解析器:输入先经 html5ever 解析,攻击者精心构造的"畸形 HTML 混淆"在解析阶段就被打回原形
- 严格的 HTML5 行为:不会自动链接化裸 URL、不会插入换行,"消毒"只做减法、不做美化
- 性能可观:README.md 中记载,相同输入下比 Python 的 bleach 快约 15 倍
默认 API 非常简单:src/lib.rs 中的clean()函数用保守默认值清洗字符串,例如<img src='' onerror=...>中的危险事件属性会被直接移除。
二、mXSS 漏洞时间线:命名空间切换是怎么绕过白名单的
mXSS(mutation XSS,突变型 XSS)的本质是:输入经清洗后被浏览器二次解析时,DOM 结构发生了变化,被删掉的危险元素"复活"了。Ammonia 历史上最棘手的漏洞几乎都来自 HTML/SVG/MathML 之间的命名空间切换。
| 版本 | 漏洞 | 说明 |
|---|---|---|
| 3.1.2 | SVG/MathML 命名空间 XSS | 清洗后出现意外的命名空间切换,可通过 svg/mathml 解析路径注入 XSS;该修复在 2.1.3 中回溯 |
| 4.1.2 | 清洗后命名空间切换 mXSS | 清洗过程移除节点后,剩余节点的命名空间关系被"重组",产生新的注入面(报告者:zzm0902@shu.edu.cn) |
| 4.1.3 | MathMLannotation-xmlmXSS | 经 MathMLannotation-xml元素在清洗后发生预期外的命名空间切换(报告者:Ivan Ivančić,经 YesWeHack 披露) |
对应修复逻辑就在 src/lib.rs 中:check_expected_namespace()(约第 2068 行)会在清洗完成后检查父子节点间的命名空间是否符合预期,凡是"在预期之外的位置发生命名空间切换"的元素一律移除——这正是 4.1.2 / 4.1.3 两次 mXSS 的补丁所在。
💡教训:白名单清洗只保证"输出时刻"的安全。只要浏览器对同一串 HTML 的解析结果可能与清洗时的 DOM 不一致,攻击面就依然存在。这也是为什么此类修复必须回溯到多个受支持大版本(4.0.1、3.3.1 均为 backport)。
三、DoS 漏洞清单:不注入脚本,也能让你的服务挂掉
🔥 拒绝服务类漏洞在 Ammonia 的"血泪史"中同样占有一席之地:
| 版本 | 问题 | 表现 |
|---|---|---|
| 2.1.0 | 病态深层嵌套输入 | 递归遍历超深 DOM 导致栈溢出,一个精心构造的超长嵌套 HTML 即可打崩服务 |
| 0.x(PR #113) | 递归析构函数 DoS | 深层嵌套在节点释放阶段触发递归销毁,造成栈溢出 |
| 3.1.1 | 非法 URL 崩溃 | 某些配置下遇到无效 URL 会 panic |
| 2.1.2 | 内存泄漏 | 特定节点类型在清洗后未被正确释放 |
防御思路(可参考 src/lib.rs 的实现):
- 用显式栈代替递归遍历——清洗主循环把子节点压入栈、迭代处理,彻底避开栈溢出
- 移除节点时批量回收——被删节点通过
removed队列迭代展开,防止递归析构 - 升级解析器依赖——4.1.3 同步升级 html5ever 至 0.37.1(当前 Cargo.toml 已升至 0.39),从解析层兜底
📌 另有两处容易被忽视的"非致命"缺陷,对内容正确性影响不小:
- 2.1.4 / 3.1.3:
class属性只按空格(0x20)切分,导致含制表符/换行的 class 值无法正确过滤 - 3.1.3:
clean_text对 FF/CR 控制字符处理不正确
四、其他安全修复速览(附赠清单)
- 0.2.0:在非默认配置下,
<object>的data属性会被当作 URL,可能造成信息泄漏 - 4.1.3:
selectedcontent元素内容始终剥离(解析器本就会用真实内容替换它) - 4.1.0:新增
style属性清洗支持,可用 CSS 白名单替代"整段删光 style" - 1.2.0:将
action、formaction、ping识别为 URL 属性,纳入协议与源过滤
完整历史见 CHANGELOG.md。
五、版本选型指南:2026 年该锁定哪个 Ammonia
📋 依据 SECURITY.md 的官方支持矩阵:
| 版本线 | 安全支持 | 选型建议 |
|---|---|---|
| 4.1(当前 4.1.3) | ✅ 支持 | 首选。包含全部 mXSS/DoS 修复,且是 4.1.3 修复后的最新稳定线 |
| 4.0 | ✅ 支持 | 4.0.1 已回溯 4.1.2 的命名空间 mXSS 修复,可作为次选 |
| 3.x | ✅ 支持 | 3.3.1 同样回溯了 mXSS 修复;适合被 MSRV 卡住的旧工具链 |
| ≤ 2.0 | ❌ 已停止 | 必须升级。4.0.0 起官方明确"安全修复不再回溯到 2.0 分支",2.x 用户将永远暴露在已知漏洞之下 |
升级前检查清单
- 确认 Rust 工具链:4.1.x 的 MSRV 为Rust 1.80(见 Cargo.toml 的
rust-version) - 锁定版本:在
Cargo.toml中写ammonia = "4.1",让 Cargo 拉取 4.1.3+ - 回归测试重点:若开启了
svg/mathml标签放行,重点验证 MathMLannotation-xml、SVG 内的iframe等命名空间跨界场景 - 压测极端输入:用超长深层嵌套 HTML 压测,确认升级后不再出现超时或崩溃
六、写在最后
Ammonia 的漏洞史是一份极好的教材:白名单清洗 ≠ 一劳永逸。命名空间切换让"清洗后 ≠ 浏览器解析后",深层嵌套让"遍历"本身成为攻击面。对使用者而言,最实际的做法只有三条——
- ✅ 只使用 4.1.x / 4.0.x / 3.x 三条受支持版本线
- ✅ 每次发布前关注 CHANGELOG.md 中的
fix:条目 - ✅ 把"不可信 HTML 清洗"纳入安全评审,而不是当成普通依赖升级
本文基于项目仓库内 README.md、CHANGELOG.md、SECURITY.md 及 src/lib.rs 整理,信息以官方发布说明为准。
【免费下载链接】ammoniaRepair and secure untrusted HTML项目地址: https://gitcode.com/gh_mirrors/am/ammonia
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考