
Cheerio 安全指南如何正确处理不可信的 HTML 标记【免费下载链接】cheerioThe fast, flexible, and elegant library for parsing and manipulating HTML and XML.项目地址: https://gitcode.com/gh_mirrors/ch/cheerioCheerio 常被用于解析由他人控制的页面内容爬虫、采集、富文本展示等场景因此明确它的能力边界与安全假设至关重要。本文以 website/src/content/docs/advanced/security.md 为核心结合 SECURITY.md、THREAT_MODEL.md 及源码实现系统讲解 Cheerio 的信任边界、XSS 注入风险点、选择器注入的防御方案、输入规模控制以及漏洞报告流程帮助你在生产环境中安全地使用 Cheerio。安全模型Cheerio 做什么不做什么Cheerio 的定位是解析 HTML/XML 字符串 → 用 jQuery 风格 API 查询和修改 DOM 树 → 序列化回标记。整个生命周期都在内存中完成永远不会执行脚本、不会求值表达式、不会访问网络。这是理解 Cheerio 安全模型的第一条铁律。从源码可以印证这一点解析入口在 src/load-parse.ts内部按选项在htmlparser2与parse5之间选择解析器两者都是纯解析器不执行任何 JavaScript序列化入口在 src/static.ts 的render()函数仅将 DOM 树拼接回字符串查询与遍历 API 同样只做树结构操作text()取的是textContent见 src/static.ts。THREAT_MODEL.md 把这一承诺写成了明确的信任边界所有输入标记传入load()、loadBuffer()、流式 API 或.html(content)、.append(content)等操作方法的 HTML/XML 字符串都被视为攻击者可控制而调用方应用代码被视为可信——即库本身不替你决定输出如何被使用。唯一的例外是fromURL辅助函数它会替你发起一次网络请求。其实现位于 src/index.ts使用undici作为 HTTP 客户端且为了不拖慢默认加载undici被惰性导入源码注释明确说明其导入成本约为 60ms 与 8MB 堆内存见 src/index.ts。也就是说fromURL是 Cheerio 主动触碰网络的唯一途径对它传入的 URL 需要格外谨慎。Cheerio 不是消毒器sanitizer这是本主题最重要的一条结论Cheerio 的输出是标记而不是安全标记。如果你加载的页面里含有script标签或onerror属性它们会原样通过解析和序列化存活下来——这对一个解析器而言是正确的行为但意味着$.html()的输出与输入同样可信或同样不可信。const $ cheerio.load(img srcx onerroralert(1)); $.html(); // 注意onerror 属性依然还在因此如果你打算把抓取来的标记渲染到浏览器中必须先经过专门的消毒器处理例如sanitize-html或 DOMPurify。Cheerio 定位是解析与操作不是 XSS 防御层。text()能救你吗只能收窄问题用text()替代html()可以把问题收窄但不能根除text()去掉的是标记的结构但返回的字符串里仍然可能包含、、这些字符。如果你把这串字符直接丢进 HTML 的 sink比如innerHTML等于把标记重新拼了回来。正确的做法是把文本写入按文本对待的位置浏览器中用textContentCheerio 中用.text()或者根据目标上下文做合适的转义。从源码看text()的 getter 行为就是拼接textContentsrc/static.ts而setter 行为则是转义——在 src/api/manipulation.ts 中.text(str)会把字符串当作纯文本写入 DOM 树序列化时特殊字符会被转义这正是它安全的原因。两种方法的分工由此在源码层面得到确认.html(str)、.append(str)等按标记解析参数 → 可注入.text(str)按文本写入 → 自动转义。操作方法的参数会被当作原始 HTML 解析html()、append()、prepend()、before()、after()、replaceWith()、wrap()这七个方法都会把字符串参数当作标记解析。传入用户输入就相当于把其中包含的任何标签直接注入进去// 如果 name 是 img srcx onerror…你刚刚注入了一个元素。 $(#greeting).html(Hello, ${name}); // text() 会转义所以这是安全的。 $(#greeting).text(Hello, ${name});源码证据在 src/api/manipulation.ts 的_makeDomArray()当参数是字符串时它直接调用this._parse(elem, this.options, false, null)把字符串解析成 DOM 节点然后插入目标位置。整个链路的起点正是文档开头声明的信任边界——所有传入这些方法的字符串都被视为标记。安全实践表达文本意图时就用text()只有当你自己构造的、或已经过消毒的内容才交给这些会解析标记的方法。由用户输入构造的选择器注入与拒绝服务把来自不可信来源的选择器当作来自不可信来源的正则表达式来对待——别用。一个精心构造的选择器既能让选择器引擎做大量工作性能消耗也可能匹配到你不希望匹配的元素。注意给值加引号远远不够。值中的会闭合属性选择器其后的所有内容都会被当作更多选择器语法解析const id x], [data-idsecret; // 变成了[data-idx], [data-idsecret] —— 两个选择器而不是一个。 $([data-id${id}]); // 匹配到了调用方本不该看到的元素。Cheerio没有CSS.escape这类工具所以可靠的修复方案是让值完全离开选择器——先用固定的选择器匹配再把属性值作为数据进行比较const matches $([data-id]).filter((_, el) $(el).attr(data-id) id);这种固定选择器 数据比较的模式还同时规避了另一类问题当类名、id 或属性值来自数据时.、:、空格、等字符都会破坏选择器语法。这一点在 website/src/content/docs/basics/troubleshooting.mdx 的 Looking something up by a value from data 一节有更完整的示例并明确指出如果该值来自不可信来源这就不只是正确性问题而是安全问题。另外THREAT_MODEL.md 的信任边界将来自外部来源的 CSS 选择器列为攻击者可控制输入理由是选择器可能触发选择器引擎的病态回溯即 ReDoS 一类的拒绝服务。如果必须接受外部选择器同样要考虑限制与校验。限制你接受的输入规模解析开销与输入大小成正比——Cheerio 会乐意去解析一个 500 MB 的响应。所以凡是接受来自你不受控来源的标记先在上游限制大小再交给 Cheerio使用fromURL时URL 本身决定了抓取什么内容。如果 URL 来自用户输入必须校验它否则你就有了一个 SSRF服务端请求伪造漏洞。注意 THREAT_MODEL.md 中关于拒绝服务的界定解析一个 500 MB 的 HTML 文件消耗大量内存是符合预期的正常行为不是漏洞一份有效的 DoS 报告必须证明资源消耗与输入大小之间是不对称的例如 ReDoS、指数级实体扩展、平方级解析行为。报告漏洞与威胁模型边界支持版本与报告流程根据 SECURITY.md版本是否支持1.x✅ 是 1.0❌ 否只有1.x分支的最新发布版本会获得安全更新使用旧的大版本请升级。报告漏洞时不要开公开的 GitHub issue应通过 Tidelift 安全联系渠道或 GitHub 的私有漏洞报告功能提交报告尽量包含漏洞描述与潜在影响、复现步骤或 PoC、受影响的 cheerio 版本、相关配置、建议的严重级别Critical / High / Medium / Low。报告后的预期流程72 小时内确认收到 → 评估严重性、影响面与受影响版本 → 开发、测试并发布补丁 → 发布 GitHub Security Advisory 公开详情可匿名。项目关注的安全问题范围SECURITY.md 明确列出项目特别关注的漏洞类型拒绝服务——导致过度内存或 CPU 消耗的构造输入如 ReDoS、平方级解析原型污染——通过解析内容或 API 误用篡改Object.prototypeXSS 促成——cheerio 的输出在浏览器中渲染时意外引入 XSS 的情形供应链——依赖、构建流水线或发布产物被篡改信息泄露——通过解析或序列化行为意外泄漏数据。范围之外调用方的责任THREAT_MODEL.md 对不属于 cheerio 安全问题的情形做了清晰界定其中最容易被误解的是对 cheerio 输出的不安全使用——把未经消毒的输出渲染到浏览器是应用的责任输入不可信时应使用专门的消毒器应用层面的 API 误用——例如用攻击者可控的字符串作为属性名访问 cheerio 对象或把未消毒的对象作为选项传入运行时/平台缺陷Node.js、V8、操作系统、受控良性的大输入、多库串联利用链gadget chaining等。这与 THREAT_MODEL.md 开头列出的三条基本假设一致Cheerio 运行在服务端 Node.js 环境而非浏览器中调用方负责在接收不可信标记时限制输入规模Cheerio 的输出是标记而非安全 HTML渲染前必须消毒。结语安全使用 Cheerio 的检查清单记住定位Cheerio 是解析器不是消毒器输出标记而非安全标记要渲染就消毒把抓取内容渲染到浏览器前先用sanitize-html/ DOMPurify 处理区分方法与意图表达文本用.text()只有自己构造或已消毒的内容才交给html()/append()/before()/wrap()等按标记解析的方法不信任外部选择器值来自不可信来源时用固定选择器 属性值数据比较代替字符串拼接选择器并警惕选择器引起的回溯开销限制输入规模上游截断不受控来源的标记大小fromURL的 URL 若来自用户必须校验防止 SSRF了解报告渠道安全问题走 SECURITY.md 的私有渠道威胁边界细节见 THREAT_MODEL.md应急响应流程见 INCIDENT_RESPONSE.md。理解并尊重这些边界Cheerio 就能安全地成为你解析与操作 HTML/XML 的可靠工具。【免费下载链接】cheerioThe fast, flexible, and elegant library for parsing and manipulating HTML and XML.项目地址: https://gitcode.com/gh_mirrors/ch/cheerio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考