外部不安全脚本注入本质是执行权失控,根源在CDN等分发环节被劫持;需通过curl抓原始响应、多节点比对、响应头分析及直连源站验证污染来源;CDN常见注入点包括cache_key含query_string、动态页缓存不当、回源未校验证书、Edge Function未转义用户输入;服务端输出须按HTML上下文(元素内容、属性、JS字符串、URL)做精准转义;DOMPurify是最可靠净化方案,需用最新版、启用SAFE_FOR_TEMPLATES、声明ALLOWED_DATA_ATTR,并与客户端配置严格一致。

外部不安全脚本注入不是代码“写得不好”,而是执行权被交出去了——<script src="https://cdn-evil.net/track.js"></script> 这种标签一旦出现在你页面的原始响应里,后续所有 HTML 结构、语义、可维护性都会崩塌。修复核心不在美化代码,而在掐断注入源头。
怎么确认是第三方脚本注入,而不是自己代码写错了
别急着改模板或加转义,先验证是否真被污染:
- 用
curl -s https://yoursite.com/ | head -n 50抓原始 HTML 响应体,搜索<script、<base、<iframe—— 如果这些标签出现在你源码里根本没写的位置,基本就是 CDN 或中间代理层被劫持 - 换不同出口 IP 测试:
curl -x http://proxy-us.example:8080 https://yoursite.com/和curl -x http://proxy-jp.example:8080 https://yoursite.com/,若仅部分节点返回异常,说明是 CDN POP 点配置漏洞 - 对比响应头:
ETag是否与源站一致;Content-Length是否明显变大(注入脚本通常增加 300–800 字节) - 临时切走 CDN(改 DNS 或 hosts 指向源站 IP),问题消失即证实是分发环节被篡改
CDN 层最常被利用的注入入口在哪
CDN 不是透明管道,它的缓存、重写、边缘计算能力本身就是攻击面:
-
cache_key包含query_string:攻击者发/index.html?x=%3Cscript%3Ealert(1)%3C/script%3E,CDN 缓存该 URL 对应的恶意响应,后续所有用户访问都中招 - 动态页未设
Cache-Control: private或no-store:登录页、订单页被缓存后注入,危害远超静态资源 - 回源未校验证书或 IP:CDN 回源时若允许 HTTP 或跳过 TLS 验证,中间人可伪造响应并注入
- Edge Function / Workers 中拼接用户输入:比如根据
request.headers.get('X-User')动态插入 DOM 片段,却没调用he.escape()或等效转义
服务端输出前必须做的 HTML 上下文感知转义
第三方脚本能注入成功,往往因为你自己的输出逻辑就存在属性注入漏洞——比如把用户输入直接拼进 input value="xxx" 或 div class="xxx",给攻击者提供闭合引号、注入事件处理器的机会:
立即学习“前端免费学习笔记(深入)”;
- 在 HTML 元素内容中(如
<div>{user_input}</div>):需转义<、>、&、"、'五个字符;Python 用html.escape(input, quote=True),PHP 用htmlspecialchars($input, ENT_QUOTES, 'UTF-8') - 在 HTML 属性中(如
<input value="{user_input}">):除上述五字符,还必须确保引号被正确包裹;Node.js 推荐he.escape()+ 协议校验(拒绝javascript:、data:) - 在 JavaScript 字符串上下文(如
const msg = "${userInput}";):必须用JSON.stringify()包裹,否则单双引号逃逸、\u2028等 Unicode 行分隔符会导致 JS 解析中断或注入 - 在 URL 参数中(如
location.href = '/search?q=' + userInput):必须用encodeURIComponent(),不能只靠 HTML 转义
DOMPurify 是当前最可靠的内容净化方案
它不是简单删标签,而是基于 HTML 解析器重建 DOM 树,再按白名单策略逐节点裁剪。相比正则过滤或黑名单清理,能防住嵌套绕过(如 <img src=1 onerror="alert(1)">)、编码混淆(如 <script></script>)、属性污染(如 style="background:url(javascript:alert())")等常见 bypass 手段:
- 必须用最新版(v3.0+),旧版本对 SVG 和 MathML 的处理存在已知绕过
- 默认配置不启用
SAFE_FOR_TEMPLATES,若用于 Vue/React 插值上下文,需显式开启 -
data-属性不做自动清理,如有业务依赖自定义data-*,需在ALLOWED_DATA_ATTR中声明 - 它不处理 CSS 注入,若允许
style属性,需额外用CSS.escape()或css-sanitize二次过滤
真正容易被忽略的是:服务端和客户端净化配置必须严格一致。否则 SSR 场景下会出现“服务端渲染正常、客户端 hydration 失败”,而这种失败往往静默发生,直到用户反馈样式错乱或功能失灵才被发现。



















