HTMLIFrameElement: srcdoc 属性

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流浏览器均已支持。

警告:此属性会将其输入解析为 HTML,并将结果写入 iframe 的 DOM。像这样的 API 被称为注入点,并且可能是跨站脚本 (XSS) 攻击的载体,如果输入最初来自攻击者。

您可以通过始终分配 TrustedHTML 对象而不是字符串并强制执行可信类型来减轻这种风险。有关更多信息,请参阅安全注意事项

HTMLIFrameElement 接口的 srcdoc 属性用于获取或设置 iframe 文档的内联 HTML 标记。

这反映了 <iframe> 元素的 srcdoc 属性。

获取此属性会返回一个包含 iframe 文档 HTML 序列化的字符串。如果未设置该值,则返回 undefined

设置此属性可以接受 TrustedHTML 对象或字符串。它会将此输入解析为 HTML 文档,并用结果替换 iframe 的内容。

异常

TypeError

如果在可信类型CSP 强制执行且未定义默认策略时将属性设置为字符串,则抛出此错误。

描述

srcdoc 属性反映了 <iframe> 元素 srcdoc 属性的内容,并且可以用于设置或获取属于 <iframe> 的 HTML 文档。

设置此属性时,输入应定义一个有效的 HTML 文档,包括 文档类型声明<html><body> 以及其他标签。但请注意,浏览器通常能容忍无效的标记,并且大多数浏览器应该会尝试渲染仅包含 body 内容的输入。

浏览器支持的任何标记都将被解析/序列化,包括Shadow 根

请注意,如果设置了此属性,它将覆盖在 src 属性中设置的任何值。

安全注意事项

srcdoc 属性默认允许任何 HTML 标记在 iframe 中运行。如果 iframe 未使用内容安全策略 (CSP) 的 sandbox 指令进行沙箱化(或已沙箱化但包含 allow-same-origin 值),则它将与父级同源。这意味着 iframe 将完全访问父级 DOM 和资源,反之亦然。

如果用户提供的潜在不安全字符串未经验证就注入到 iframe 中,这将是跨站脚本 (XSS) 攻击的重要载体。考虑以下代码,其中来自用户的 HTML 字符串可能被传递到一个 iframe 中,然后该 iframe 被添加到文档中。

js
const untrustedStringFromUser = `<!doctype html><script src="http://evil.com/naughty.js"></script>`;
const iframe = document.createElement("iframe");
iframe.srcdoc = untrustedStringFromUser;
document.body.appendChild(iframe);

如果 iframe 不需要访问您的父文档,您可以通过使用不包含 allow-same-origin 值的 CSP 沙箱来降低风险。然后,iframe 将被视为跨域资源,攻击将受到显著限制。您也可以使用更通用的 CSP 来限制允许从何处获取脚本和其他资源。

您可以始终将 TrustedHTML 对象而不是字符串赋给 srcdoc,并使用 require-trusted-types-for CSP 指令强制执行可信类型,以进一步降低风险。这可确保输入通过转换函数,该函数有机会清理输入,在注入之前删除潜在的危险标记。

示例

从 iframe 读取 HTML

读取 srcdoc 会导致用户代理序列化 iframe 的文档。

给定以下 HTML

html
<frame
  id="example"
  srcdoc="<!doctype html><body><p>Hello World!</p></body>"></frame>

您可以按如下所示获取并记录标记

js
const frame = document.querySelector("#frame");
const frameDoc = frame.srcdoc;
console.log(frameDoc); // "<!doctype html><body><p>Hello World!</p></body>"

替换 iframe 的内联源

在此示例中,我们将通过将 HTML 分配给 iframe 的 srcdoc 属性来替换其文档。为降低 XSS 风险,我们将首先从包含 HTML 的字符串创建 TrustedHTML 对象,然后将该对象分配给 srcdoc

Trusted Types 尚未在所有浏览器中得到支持,因此我们首先定义可信类型 tinyfill。它充当可信类型 JavaScript API 的透明替代品。

js
if (typeof trustedTypes === "undefined")
  trustedTypes = { createPolicy: (n, rules) => rules };

接下来,我们创建一个 TrustedTypePolicy,它定义了一个 createHTML() 方法,用于将输入字符串转换为 TrustedHTML 实例。通常,createHTML() 的实现会使用像 DOMPurify 这样的库来清理输入,如下所示。

js
const policy = trustedTypes.createPolicy("my-policy", {
  createHTML: (input) => DOMPurify.sanitize(input),
});

然后我们使用这个 policy 对象从潜在不安全的输入字符串创建 TrustedHTML 对象,并将结果分配给元素。

js
// The potentially malicious string
const untrustedString =
  "<!doctype html><body><p>I might be XSS</p><img src='x' onerror='alert(1)'></body>";

// Create a TrustedHTML instance using the policy
const trustedHTML = policy.createHTML(untrustedString);

// Inject the TrustedHTML (which contains a trusted string)
const frame = document.querySelector("#frame");
const frameDoc = frame.srcdoc;

警告:虽然您可以直接将字符串赋给 srcdoc,但如果待插入的字符串可能包含潜在的恶意内容,这是一种安全风险。您应该使用 TrustedHTML 来确保在插入内容之前对其进行清理,并且应该设置 CSP 标头来强制执行可信类型

规范

规范
HTML
# dom-iframe-srcdoc

浏览器兼容性