Angular中直接绑定innerHTML会因安全策略被拦截,必须用DomSanitizer.bypassSecurityTrustHtml()标记可信内容,但该方法不替代HTML净化,用户输入需先经DOMPurify清洗。

直接用 innerHTML 注入模板内容必然失败
浏览器对 <template> 标签的 content 属性做了只读限制,它返回的是一个 DocumentFragment。你不能反复往 DOM 里 append 同一个 fragment——第一次成功后,它就变空了,第二次 append 什么都不会出现,也不会报错,静默失败。
常见错误写法:el.appendChild(tmpl.content),只要调用两次,第二回就啥也不渲染。
- 必须显式调用
cloneNode(true)才能复用:每次插入前都得tmpl.content.cloneNode(true) -
tmpl.content上无法直接绑定事件,addEventListener必须在克隆后的节点上调用 - 模板里的
<slot>在普通 DOM 中完全无效,只对ShadowRoot生效;沙箱若非 Shadow DOM,则 slot 会被忽略
服务端模板输出 HTML 到沙箱时必须绕过自动转义
Go 的 html/template、Django 或 Jinja2 默认会对所有变量做 HTML 转义,这在沙箱环境(如 iframe.contentDocument 或微前端子应用)中会破坏结构:比如 <script> 变成 <script></script>,导致脚本不执行、样式丢失、事件失效。
正确做法不是关全局转义,而是用类型标记明确声明可信内容:
立即学习“前端免费学习笔记(深入)”;
- Go 中用
template.HTML类型包装后端构造的 HTML(如Script: template.HTML(`<script>...</script>`)),再在模板中用{{.Script}}输出 - 该值必须 100% 来自后端可控逻辑,绝不能拼接用户输入
- 若沙箱启用了 CSP,
unsafe-inline会被拦截,此时应改用带nonce的外部 script 或预编译函数
Angular 里用 DomSanitizer.bypassSecurityTrustHtml() 不等于安全
Angular 的 safeHtml 管道只是告诉框架“我信任这段 HTML”,它不净化、不校验、不过滤。如果传入的是用户输入或未清洗的富文本,XSS 风险照旧存在。
典型误用:把 API 返回的 raw HTML 直接塞进 [innerHTML] 绑定,哪怕加了 | safeHtml 管道。
- 管道只解决 Angular 的安全拦截,不替代白名单净化
- 富文本场景必须前置使用
DOMPurify.sanitize()处理字符串,再传给bypassSecurityTrustHtml() - 含 Angular 指令(如
<mat-expansion-panel>)的 HTML 字符串不会被编译,动态注入后组件不初始化、CSS/动画全失效
在线运行工具(CodePen/JSFiddle)中动态加载第三方库要避开 document.createElement('script')
这类平台默认把 JS 面板当普通脚本执行,且沙盒环境会拦截 eval、禁用全局污染、重写 script 路径。用 document.createElement('script') 加载后立刻调用 Chart.js 或 Lodash,大概率报 ReferenceError。
根本原因:脚本异步加载完成时机不可控,且在线工具不保证全局上下文一致性。
- 唯一可靠方式是用 ES Module:在 JS 面板顶部设为 “Module” 模式,然后
await import('https://cdn.jsdelivr.net/npm/chart.js@4.4.0/+esm') - jQuery 等无原生 ESM 的库,改用 Skypack:
await import('https://cdn.skypack.dev/jquery@3.7.1') - 切忌在非模块上下文中写
import,否则报Cannot use import statement outside a module



















