从 object 到 iframe — 通用嵌入技术

开发者通常认为将图像、视频和音频等多媒体嵌入到网页中。在本文中,我们将另辟蹊径,探讨一些允许您将各种内容类型嵌入到网页中的元素:<iframe><embed><object> 元素。<iframe> 用于嵌入其他网页,而另外两个元素允许您嵌入外部资源,例如 PDF 文件。

预备知识 已安装基本软件,了解文件操作基础知识,熟悉 HTML 基础知识
目标 了解如何使用 <object><embed><iframe> 将项目(例如 PDF 文档和其他网页)嵌入到网页中。

嵌入简史

很久以前,在万维网上,使用框架(frames)来创建网站很流行——网站的小部分存储在单独的 HTML 页面中。这些页面嵌入在一个称为框架集(frameset)的主文档中,您可以通过框架集指定每个框架在屏幕上占据的区域,就像调整表格的列和行大小一样。在 90 年代中后期,这些被认为是酷炫的巅峰,并且有证据表明,将网页分成这样的小块对下载速度更有利——尤其是在当时的网速如此缓慢的情况下。然而,它们确实存在许多问题,随着网速的加快,这些问题远远超过了任何优点,所以你现在已经看不到它们被使用了。

稍后(90 年代末,2000 年代初),插件技术变得非常流行,例如 Java AppletFlash——这些技术允许 Web 开发人员将视频和动画等富内容嵌入到网页中,而这些内容仅通过 HTML 是无法实现的。嵌入这些技术是通过 <object> 元素和较少使用的 <embed> 元素实现的,它们在当时非常有用。由于可访问性、安全性、文件大小等诸多问题,它们此后已不再流行。如今,主流浏览器已停止支持 Flash 等插件。

最后,<iframe> 元素出现了(以及其他嵌入内容的方式,例如 <canvas><video> 等)。它提供了一种将整个 Web 文档嵌入到另一个文档中的方法,就好像它是一个 <img> 或其他类似元素一样,并且如今仍被经常使用。

历史课到此为止,让我们继续看看如何使用其中一些。

使用经典嵌入功能

在本文中,我们将直接进入一个练习,让您立即了解嵌入技术有什么用。在线世界对 YouTube 非常熟悉,但许多人不知道它拥有的一些共享功能。

  1. 首先,打开 MDN Playground
  2. 现在我们将了解 YouTube 如何允许我们使用 <iframe> 将视频嵌入到我们喜欢的任何页面中。
    1. 前往 YouTube,找到您喜欢的视频。
    2. 在视频下方,您会找到一个“分享”按钮——选择此按钮以显示分享选项。
    3. 选择“嵌入”按钮,您将获得一些 <iframe> 代码——复制此代码。
    4. 将其粘贴到 Playground 中的 HTML 面板中,并查看输出结果。
  3. 作为额外加分项,您还可以尝试在 Playground 中嵌入 Google 地图
    1. 前往 Google 地图,找到您喜欢的地图。
    2. 单击 UI 左上角的“汉堡包菜单”(三条水平线)。
    3. 选择“分享或嵌入地图”选项。
    4. 选择“嵌入地图”选项,这将为您提供一些 <iframe> 代码——复制它。
    5. 将其粘贴到 Playground 中的 HTML 面板中,并查看输出结果。

如果您犯了错误,您可以随时使用 Playground 中的“重置”按钮将其重置。

iframe 详解

所以,这既简单又有趣,对吧?<iframe> 元素旨在允许您将其他 Web 文档嵌入到当前文档中。这对于将您可能无法直接控制且不想自己实现第三方内容(例如在线视频提供商的视频、Disqus 等评论系统、在线地图提供商的地图、广告横幅等)整合到您的网站中非常有用。甚至您在本课程中使用的实时可编辑示例也是使用 <iframe> 实现的。

在深入使用 <iframe> 元素之前,需要注意一些安全问题。假设您想使用 <iframe> 元素在您的一个网页中包含 MDN 词汇表,您可能会尝试类似以下代码示例。如果您将以下代码添加到您的一个页面中,您可能会惊讶地看到错误消息而不是词汇表页面。

html
<iframe
  src="https://mdn.org.cn/en-US/docs/Glossary"
  width="100%"
  height="500"
  allowfullscreen
  sandbox>
  <p>
    <a href="/en-US/docs/Glossary">
      Fallback link for browsers that don't support iframes
    </a>
  </p>
</iframe>
css
iframe {
  border: none;
}

如果您查看浏览器的控制台,您会看到如下错误消息:

Refused to display 'https://mdn.org.cn/' in a frame because it set 'X-Frame-Options' to 'deny'.

下面的安全部分将详细说明您看到此错误的原因,但首先,让我们看看我们的代码正在做什么。

该示例包含了使用 <iframe> 所需的基本要素:

边框:无

如果使用,<iframe> 将不带边框显示。否则,默认情况下,浏览器会显示带边框的 <iframe>(这通常是不需要的)。

allowfullscreen

如果设置,<iframe> 可以使用 Fullscreen API 进入全屏模式(这超出了本文的范围)。

src

此属性与 <video>/<img> 一样,包含指向要嵌入文档 URL 的路径。

widthheight

这些属性指定了您希望 iframe 的宽度和高度。

sandbox

此属性在比其他 <iframe> 功能稍新的浏览器(例如 IE 10 及更高版本)中起作用,它要求提高安全设置;我们将在下一节中详细介绍。

注意:为了提高速度,最好在主内容加载完成后使用 JavaScript 设置 iframe 的 src 属性。这可以使您的页面更快可用,并减少您的官方页面加载时间(一个重要的 SEO 指标)。

安全问题

前面我们提到了安全问题——现在让我们更详细地探讨一下。我们不期望您第一次就完全理解所有这些内容;我们只是想让您意识到这个问题,并提供一个参考,以便您在变得更有经验并开始在实验和工作中考虑使用 <iframe> 时可以回顾。另外,没有必要害怕而不使用 <iframe>——您只需要小心谨慎。请继续阅读……

浏览器制造商和 Web 开发人员已经通过惨痛的教训认识到,iframe 是 Web 上不法分子(通常称为黑客,或更准确地说,破解者)进行攻击的常见目标(官方术语:攻击向量),如果他们试图恶意修改您的网页,或者诱骗人们做他们不想做的事情,例如泄露用户名和密码等敏感信息。正因为如此,规范工程师和浏览器开发人员为使 <iframe> 更安全开发了各种安全机制,并且还有一些最佳实践需要考虑——我们将在下面介绍其中一些。

注意:点击劫持是一种常见的 iframe 攻击,黑客将一个不可见的 iframe 嵌入到您的文档中(或者将您的文档嵌入到他们自己的恶意网站中),并利用它来捕获用户的交互。这是一种误导用户或窃取敏感数据的常见方式。

不过,首先举一个简单的例子——尝试在您的浏览器中加载我们前面展示的那个例子——您可以在 GitHub 上找到它的在线版本也可查看源代码)。您可能会看到一些类似于“我无法打开此页面”的错误消息,而不是您期望的页面,如果您查看浏览器开发者工具中的控制台,您会看到一条消息告诉您原因。在 Firefox 中,您会收到类似“加载 ‘https://mdn.org.cn/en-US/docs/Glossary’ 的框架被 'X-Frame-Options' 指令设置为 'DENY' 拒绝”的消息。这是因为构建 MDN 的开发者在提供网站页面的服务器上包含了一个设置,禁止将它们嵌入到 <iframe> 中(参见下面的配置 CSP 指令)。这是有道理的——除非您想做一些事情,例如将它们嵌入到您的网站并声称是您自己的——或尝试通过点击劫持窃取数据,否则将整个 MDN 页面嵌入到其他页面中并没有真正的意义,这些都是非常糟糕的事情。此外,如果每个人都开始这样做,所有额外的带宽将开始让 Mozilla 花费大量资金。

仅在必要时嵌入

有时嵌入第三方内容(例如 YouTube 视频和地图)是有意义的,但如果您只在完全必要时才嵌入第三方内容,可以省去很多麻烦。一个好的网络安全规则是:“你永远不能过于谨慎。如果你自己制作了它,无论如何都要仔细检查。如果是别人制作的,则在证明它无害之前,假定它是危险的。”

除了安全问题,您还应该注意知识产权问题。大多数内容都受版权保护,无论是离线还是在线,甚至您可能不期望的内容(例如,维基共享资源上的大多数图片)。除非您拥有该内容或所有者已明确书面许可,否则切勿在您的网页上显示内容。侵犯版权的处罚是严厉的。再次强调,您永远不能过于谨慎。

如果内容已获得许可,您必须遵守许可条款。例如,MDN 上的内容根据 CC-BY-SA 许可。这意味着,当您引用我们的内容时,即使您进行了实质性更改,也必须正确注明出处

使用 HTTPS

HTTPSHTTP 的加密版本。您应该尽可能使用 HTTPS 提供您的网站服务。

  1. HTTPS 降低了远程内容在传输过程中被篡改的可能性。
  2. HTTPS 阻止嵌入内容访问父文档中的内容,反之亦然。

启用 HTTPS 需要安装特殊的安全证书。许多托管服务提供商提供 HTTPS 托管,您无需自行设置证书。但是,如果您确实需要自行设置网站的 HTTPS 支持,Let's Encrypt 提供工具和说明,您可以用于自动创建和安装必要的证书——内置支持最广泛使用的 Web 服务器,包括 Apache Web 服务器、Nginx 等。Let's Encrypt 工具旨在使过程尽可能简单,因此确实没有充分的理由避免使用它或其他可用方式来启用您的网站 HTTPS。

注意:GitHub Pages 默认通过 HTTPS 提供内容。如果您使用不同的托管服务提供商,您应该检查他们对通过 HTTPS 提供内容的支持情况。

始终使用 sandbox 属性

您希望尽可能减少攻击者在您网站上作恶的能力,因此您应该只给予嵌入内容完成其工作所需的权限。当然,这也适用于您自己的内容。一个可以适当地使用代码——或用于测试——但不会对代码库的其余部分造成任何损害(无论是意外还是恶意)的代码容器被称为沙盒

未沙盒化的内容可能能够执行 JavaScript、提交表单、触发弹出窗口等。默认情况下,您应该通过使用不带参数的 sandbox 属性来施加所有可用限制,如我们之前的示例所示。

如果绝对需要,您可以逐个添加权限(在 sandbox="" 属性值内)——有关所有可用选项,请参阅 sandbox 参考条目。一个重要的注意事项是,您永远不应同时向 sandbox 属性添加 allow-scriptsallow-same-origin——在这种情况下,嵌入内容可以绕过阻止站点执行脚本的同源策略,并使用 JavaScript 完全关闭沙盒。

注意:如果攻击者能够诱骗人们直接访问恶意内容(在 iframe 之外),沙盒将无法提供保护。如果某些内容(例如用户生成的内容)有任何恶意可能性,请务必从与您主站点不同的提供服务。

配置 CSP 指令

CSP 代表内容安全策略,它提供一组 HTTP 头(当您的网页从 Web 服务器提供时,与网页一起发送的元数据),旨在提高 HTML 文档的安全性。在保护 <iframe> 方面,您可以配置您的服务器发送适当的 X-Frame-Options 头。这可以防止其他网站在他们的网页中嵌入您的内容(这将启用点击劫持和许多其他攻击),这正是 MDN 开发人员所做的,正如我们之前所见。

注意:您可以阅读 Frederik Braun 的文章 关于 X-Frame-Options 安全头,以获取更多有关此主题的背景信息。显然,本文无法对此进行全面解释。

<embed><object> 元素

<embed><object> 元素的功能与 <iframe> 不同——这些元素是用于嵌入外部内容(如 PDF)的通用嵌入工具。

然而,您不太可能经常使用这些元素。如果您需要显示 PDF,通常最好链接到它们,而不是将它们嵌入到页面中。

从历史上看,这些元素也曾用于嵌入由浏览器插件(如 Adobe Flash)处理的内容,但这项技术现在已经过时,并且不受现代浏览器支持。

如果您发现自己需要嵌入插件内容,那么您至少需要以下信息:

<embed> <object>
URL 指向嵌入内容 src data
嵌入内容的准确媒体类型 type type
由插件控制的框的高度和宽度(以 CSS 像素为单位) height
width
height
width
作为不可用资源的备用独立 HTML 内容 不支持(<noembed> 已过时) 包含在 <object> 的开始和结束标签之间

让我们看一个嵌入 PDF 到页面中的 <object> 示例(参见实时示例源代码

html
<object data="my-pdf.pdf" type="application/pdf" width="800" height="1200">
  <p>
    You don't have a PDF plugin, but you can
    <a href="my-pdf.pdf">download the PDF file. </a>
  </p>
</object>

PDF 曾是纸质文档和数字文档之间必不可少的过渡,但它们带来了许多可访问性挑战,并且在小屏幕上可能难以阅读。它们在某些圈子中仍然很受欢迎,但最好是链接到它们,以便可以在单独的页面上下载或阅读,而不是将它们嵌入到网页中。

总结

在网页文档中嵌入其他内容这个话题很快就会变得非常复杂,因此在本文中,我们试图以一种简单、熟悉的方式介绍它,使其立即显得相关,同时仍暗示了所涉及技术的一些更高级功能。一开始,您不太可能将嵌入用于除了在页面中包含地图和视频等第三方内容之外的任何其他用途。然而,随着您经验的增长,您可能会发现它们有更多的用途。

除了我们在这里讨论的技术之外,还有许多其他涉及嵌入外部内容的技术。我们在前面的文章中看到了一些,例如 <video><audio><img>,但还有其他一些可以发现,例如用于 JavaScript 生成的 2D 和 3D 图形的 <canvas>,以及用于嵌入矢量图形的 <svg>