☰
如何编写自定义净化规则:Ammonia attribute_filter 回调与高级 API 完全参考
2026/9/26 0:56:54 网站建设 项目流程

如何编写自定义净化规则:Ammonia attribute_filter 回调与高级 API 完全参考

【免费下载链接】ammoniaRepair and secure untrusted HTML项目地址: https://gitcode.com/gh_mirrors/am/ammonia

🧹Ammonia是 Rust 生态中最主流的 HTML 净化(sanitization)库,基于白名单机制防止 XSS、布局破坏和点击劫持。而真正发挥它威力的,是它的高级 API——尤其是attribute_filter回调:它让你对每一个属性做"放行、删除、改写"三级精细控制。本指南带你从零理解回调时机、返回值语义,并串联起url_relative、set_tag_attribute_value等配套高级 API,给出一套可直接套用的自定义净化规则配方。

一、先理解 Ammonia 的净化流程

在使用attribute_filter之前,先搞清楚它在整个流程中的位置,这是避免踩坑的关键:

步骤发生了什么你能干预的 API
1️⃣ 解析按 HTML5 规范解析为 DOM(与浏览器一致)—
2️⃣ 标签过滤移除白名单外的标签tags/clean_content_tags
3️⃣ 属性过滤移除白名单外的属性generic_attributes/tag_attributes
4️⃣回调改写attribute_filter对每个存活的属性依次调用attribute_filter⭐
5️⃣ URL 处理校验 scheme、处理相对 URLurl_schemes/url_relative
6️⃣ 序列化输出安全的 HTML 字符串—

💡 核心结论:attribute_filter只在属性已通过白名单之后才会被调用。它管不了被白名单挡掉的属性,但对通过的属性拥有最终决定权。

实现源码位于 src/lib.rs。

二、attribute_filter 回调签名解析

pub fn attribute_filter<'cb, CallbackFn>(&mut self, callback: CallbackFn) -> &mut Self where CallbackFn: for<'u> Fn(&str, &str, &'u str) -> Option<Cow<'u, str>> + Send + Sync + 'static,

回调接收三个参数,返回值决定属性的命运:

  • element(&str):元素名,如"img"、"a"
  • attribute(&str):属性名,如"src"、"href"
  • value(&str):属性当前值
  • 返回Some(value):放行(可原样返回,也可返回改写后的新值)
  • 返回None:删除该属性

两个必须知道的细节

  1. 只能设置一次。重复调用会触发 panic,源码中写得很直白:"attribute_filter can be set only once"(见 src/lib.rs)。
  2. 回调执行时机早于url_relative()。官方文档明确说明:"Rewriting of attributes with URLs is done beforeurl_relative()"。也就是说,你在回调里改写的 URL 之后还会经过相对 URL 解析。官方测试url_filter_absolute/url_filter_relative就验证了这一点:回调把src="imgtest"改写为相对路径后,RewriteWithBase再把它拼接成绝对地址(见 src/lib.rs)。

三、三个经典实战模式

模式 1:按元素+属性删除(最常用)

场景:允许图片,但想强制禁止外链图片,删除所有<img>的src:

use ammonia::Builder; let clean = Builder::new() .attribute_filter(|element, attribute, value| { match (element, attribute) { ("img", "src") => None, // 删除 _ => Some(value.into()), // 其余放行 } }) .clean("<a href=/><img alt=Home src=foo></a>"); // 输出:<a href="/"><img alt="Home"></a>

这是官方文档中的标准示例,完整代码见 src/lib.rs。

模式 2:改写属性值(URL 补全/代理化)

场景:把用户上传的相对图片路径统一改写为 CDN 地址:

let clean = Builder::new() .attribute_filter(|elem, attr, value| { match (elem, attr) { ("img", "src") => { Some(format!("https://cdn.example.com/{}", value).into()) } ("a", "href") => { // 统一加前缀走站内路由 Some(format!("/posts/{}", value).into()) } _ => Some(value.into()), } }) .clean("<img src=a.png>"); // 输出:<img src="https://cdn.example.com/a.png">

模式 3:内容级校验(白名单 + 回调双层防御)

白名单控制"哪些属性能出现",回调控制"值是否合法",两者叠加才是完整防线:

let clean = Builder::new() .tag_attributes(maplit::hashmap!["iframe" => maplit::hashset!["src"]]) .attribute_filter(|elem, attr, value| { if elem == "iframe" && attr == "src" { // 只允许来自可信域名的 iframe return value.starts_with("https://partner.example.com/") .then(|| value.into()); } Some(value.into()) }) .clean("<iframe src=\"https://evil.com/x\"></iframe>");

🛡️ 安全提示:回调里做域名白名单校验时,建议解析出 host 再精确比对,避免"https://evil.comexample.com"这类前缀绕过。

四、配套高级 API 速查表

attribute_filter很少孤军奋战,下面这些高级 API 常与它搭配使用(全部定义在 src/lib.rs 中):

API作用典型场景
url_relative(UrlRelative)控制相对 URL:透传 / 基于基准改写 / 拒绝回调改写 URL 后的"第二步",见 src/lib.rs
set_tag_attribute_value为指定标签强制注入属性值(可覆盖用户输入)给所有<a>强制加target="_blank"
allowed_classes/add_allowed_classes按标签白名单放行 CSS 类名评论系统只放行少数排版类
generic_attribute_prefixes放行所有指定前缀的属性(如data-)放行前端框架的data-*属性
link_rel控制<a>的rel属性(默认noopener noreferrer)关闭自动 rel 注入
strip_comments是否剥离 HTML 注释保留模板注释的调试模式
id_prefix给所有id加统一前缀防冲突多组件页面防 id 污染

推荐组合拳

let sanitizer = Builder::default() // 强制给外链加 target="_blank" .set_tag_attribute_value("a", "target", "_blank") // 放行 contenteditable="false">【免费下载链接】ammoniaRepair and secure untrusted HTML项目地址: https://gitcode.com/gh_mirrors/am/ammonia

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询