srcdoc 渲染富文本预览必须配 sandbox="" 并使用 DOMPurify 净化内容,且需构造完整 HTML 文档结构(含 DOCTYPE、html、head、body),所有用户输入须转义,IE 需降级处理。

srcdoc 渲染富文本预览本身不防跨站攻击——它默认完全开放执行权限,不加 sandbox 就等于主动把 XSS 入口焊死在页面上。
srcdoc 必须是完整 HTML 文档,不是富文本片段
直接把用户提交的富文本(比如 <p><strong>Hello</strong></p>)塞进 srcdoc 会出问题:Safari 空白、Chrome 渲染异常、Firefox 行为不一致。因为 srcdoc 不接受 HTML 片段,它等价于整个 iframe 的源码文件。
- ✅ 正确结构必须包含
<!DOCTYPE html>、<html>、<head>、<body>,缺一不可 -
<title>虽非强制,但缺失时旧版 Safari 可能拒绝渲染 - 未闭合标签(如
<div>hello)会导致后续内容被丢弃 - 所有双引号需转义或改用单引号包裹整个属性,否则字符串提前截断
富文本必须先净化,再内联到 srcdoc 中
即使你控制了富文本输入来源,只要含用户可控内容(比如评论区、编辑器预览),就存在 DOM 型 XSS 风险。不能靠“我只允许 <b><i>”这种黑名单过滤,得用上下文感知的净化。
- 服务端或前端都应使用
DOMPurify.sanitize(html, { ALLOWED_TAGS: ['p','br','strong','em','ul','ol','li'], ALLOWED_ATTR: ['class'] })过滤,禁用所有on*事件和javascript:协议 - CSS 必须内联到
<style>标签里,不能留style="..."属性——否则攻击者可注入style="background:url(javascript:alert(1))" - 禁止在
srcdoc字符串中拼接任何未净化的用户输入;哪怕只是插入一个昵称,也要先 HTML 属性编码(he.escape()或等效函数)
sandbox 是必配项,空值才是安全起点
不设 sandbox 的 srcdoc 会默认启用脚本执行、表单提交、弹窗、window.open()、甚至尝试修改 top.location——这跟把用户输入直接 innerHTML 到主页面一样危险。
立即学习“前端免费学习笔记(深入)”;
- ✅ 最小权限:用
sandbox=""完全禁用所有特权(脚本、表单、弹窗、存储访问全部关闭) - ⚠️ 若预览需交互(如按钮点击计数),仅加
sandbox="allow-scripts",绝不要加allow-same-origin——它会让 iframe 内脚本读取父页 DOM - 想从 iframe 调用父页方法?只能用
window.parent.postMessage(),不能依赖window.parent.xxx - 注意:
sandbox对srcdoc同样生效,不是“内联就免疫”
IE 不支持 srcdoc,降级路径不能省
IE(包括 IE11)完全忽略 srcdoc 属性,既不报错也不 fallback,结果就是 iframe 空白。这不是兼容性问题,是彻底不识别。
- 必须同时设置
src和srcdoc:现代浏览器优先用srcdoc,IE 回退到src="data:text/html;base64,..."或指向一个静态 HTML 文件 - 用
data:URL 降级时注意长度限制(部分浏览器约 2MB),富文本过长可能截断 - 动态更新
srcdoc会重载整个 iframe 上下文——已绑定事件、定时器、console 输出全丢,不适合高频预览(如 Markdown 实时键入)
真正容易被忽略的是:srcdoc 的安全边界只在“文档加载完成前”有效。一旦你为了功能加了 allow-scripts,就必须确保内联 JS 本身不引入新风险——比如没做 postMessage 权限校验、没限制接收消息来源、或者把用户输入直接拼进 eval()。沙箱不是保险柜,是带锁的玻璃房。



















