在当今的网络世界中营造丰富的体验几乎不可避免地涉及到 嵌入组件和内容,而您无法真正控制这些组件和内容。 第三方微件可以提高互动度, 而用户生成的内容有时更为重要 与网站的原生内容相比不明智的做法是放弃其中之一 都会增加网站发生“Something BadTM”问题的风险。每个 您嵌入的每个广告、每个社交媒体微件, 攻击途径:
内容安全政策 (CSP) 通过提供 让您可以将特别受信任的脚本来源和其他来源 内容。这是朝着正确方向迈出的重要一步,但需要注意的是, 大多数 CSP 指令提供的保护是二进制的:资源是 或者不允许。有时候,“我不是 我确实信任这些内容来源,但它实在是太美了!嵌入 但请不要让它破坏我的网站。”
最小权限
从本质上讲,我们正在寻找一种机制, 只嵌入工作所需的最低级别功能。如果微件 无需弹出新窗口,取消对 window.open 的访问权限 伤害。如果广告素材不需要 Flash,则关闭插件支持 问题。如果遵循最低限度原则,我们就会尽可能地确保安全 权限和屏蔽设置 与我们希望的功能不直接相关的每个功能 使用。如此一来,我们就不再需要盲目相信某些部分, 不会利用本不应使用的权限。它 他们最初根本无法使用这项功能
若要构建此类解决方案的良好框架,首先要做的就是使用 iframe
元素。
在 iframe
中加载一些不受信任的组件可以衡量隔离度
您的应用与您要加载的内容之间。加框链接的内容
将无法访问页面的 DOM,也无权访问您本地存储的数据
能够在网页上的任意位置画像;它的使用范围仅限于
轮廓线不过,这种分离并不真正可靠。包含的页面
仍然有许多令人厌烦或恶意行为的选项:自动播放
视频、插件和弹出式窗口只是冰山一角。
iframe
元素的 sandbox
属性
提供了加强对加框内容的限制所需的工具。我们可以
指示浏览器以较低权限加载特定框架的内容
从而仅允许执行任何操作所需的功能子集
需要完成哪些工作
Twust,但是验证
Twitter 的“Tweet”按钮就是一个很好的功能示例, 安全地嵌入到您的网站上Twitter 允许您将 通过 iframe 加载按钮 替换为以下代码:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
为了弄清楚我们可以锁定什么,让我们仔细研究一下 按钮要求。加载到框架中的 HTML 会执行一些 并生成填充 推送至 Twitter 微博界面。该界面需要访问 Twitter 的 Cookie 以将 Twitter 微博与正确的账号关联起来,并且需要能够 提交推文表单差不多就是这样。帧不需要 无需打开顶级窗口或加载任何插件 很多其他功能。由于它不需要这些权限 就可以通过将框架的内容沙盒化来移除它们
沙盒基于白名单运行。首先,我们先删除所有
然后重新启用各项功能,只需添加
指定标记来指定沙盒的配置。对于 Twitter 微件,我们
决定启用 JavaScript、弹出式窗口、表单提交和 twitter.com 的
Cookie。为此,我们可以在 iframe
中添加 sandbox
属性,并使用
以下值:
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
大功告成。我们已经赋予框架所需的全部功能
浏览器会拒绝它访问我们未授权的
通过 sandbox
属性的值明确授予该权限。
对功能进行精细控制
在上面的示例中,我们看到了一些可能的沙盒标记, 更加详细地探讨属性的内部运作方式。
如果 iframe 的沙盒属性为空,则加框文档将完全经过沙盒化处理,使其遵循 并受到以下限制:
- JavaScript 不会在框架文档中执行。这不仅包括 JavaScript 通过脚本代码明确加载,还通过内嵌事件处理脚本加载 和 javascript: 网址。这也意味着,noscript 标记中包含的内容 将会显示,这与用户自己停用了脚本一样。
- 加框文档会加载到一个唯一的源,这意味着: 同源检查将失败;唯一的源与其他任何源都不匹配, 甚至包括自身。这意味着文档内容 访问存储在任何源的 Cookie 或任何其他存储机制中的数据 (DOM 存储、索引型数据库等)。
- 框架文档无法新建窗口或对话框(通过
window.open
或target="_blank"
)。 - 无法提交表单。
- 插件无法加载。
- 带框架的文档只能自行导航,无法为其顶级父级导航。
设置
window.top.location
会抛出异常,点击包含以下内容的链接target="_top"
不会产生任何影响。 - 自动触发的功能(自动聚焦的表单元素、自动播放) 视频等内容)都会遭到屏蔽。
- 无法获取指针锁。
- 在框架文档包含的
iframes
上,系统会忽略seamless
属性。
这非常严肃,将一个文档加载到完全沙盒化的 iframe
中。
风险也很小当然,它也没有太多价值:
也许能够针对某些静态内容使用完整的沙盒
多数时候你就要放宽一些要求
除插件外,所有这些限制都可以通过 向沙盒属性的值添加标记。沙盒化文档永远不会 运行插件,因为插件是未经过沙盒化的原生代码,但其他一切都是正常的 游戏:
allow-forms
允许提交表单。allow-popups
允许显示弹出式窗口。allow-pointer-lock
允许(令人惊讶!)指针锁定。allow-same-origin
允许文档保留其来源;已加载页面 来自https://example.com/
的个人资料将保留对该来源的数据的访问权限。allow-scripts
允许执行 JavaScript,还允许一些功能 (因为通过 JavaScript 实现起来很简单)。allow-top-navigation
可让文档按照 打开顶级窗口
了解了这一点,我们就可以准确评估最终为什么 上述 Twitter 示例中的一组沙盒标记:
allow-scripts
是必需的,因为加载到框架中的页面会运行一些 JavaScript 来处理用户互动。allow-popups
是必需的,因为按钮会在新的 窗口。allow-forms
为必填项,因为推文表单应可提交。allow-same-origin
是必要的,否则 twitter.com 的 Cookie 无法访问,且用户无法登录以发布表单。
需要注意的一点是
适用于在沙盒中创建的任何窗口或框架。这意味着
将 allow-forms
添加到帧的沙盒中,即使该表单仅存在
弹出窗口的窗口
有了 sandbox
属性后,widget 将仅获得其
并且仍会保留插件、顶部导航和指针锁定等功能
已被屏蔽。我们降低了嵌入 widget 的风险,而且没有造成任何不良影响。
对所有人来说,这都是一个赢家。
权限分离
对第三方内容进行沙盒化,以便在 显然,低特权环境非常有用但您的 ?你相信自己,对吧?那么为什么要担心沙盒呢?
我来回答这个问题:如果您的代码不需要插件, 对插件的访问权限?最好的情况是,你永远都用不到这种特权,最坏的则是你 这样可能会使攻击者有机会一探究竟。每个人的代码都有 而且几乎所有应用都容易受到在某一方面被利用的漏洞 或其他。将您自己的代码沙盒化意味着即使攻击者成功地 破坏您的应用,他们不会获得完整 应用的来源;他们将只能执行应用能够执行的操作 用途。这也不错,但还不如预期。
您可以将应用拆分为 逻辑部分,并以尽可能小的权限对每个部分进行沙盒化。 这种方法在原生代码中很常见:例如,Chrome 会自行破坏自身 访问本地硬盘的高权限浏览器进程, 并能建立网络连接和许多低权限渲染程序进程, 轻松解析不受信任的内容。渲染程序无需触摸屏幕 浏览器会负责为他们提供 呈现网页。即使聪明的黑客找到了破坏渲染程序的方法, 因为渲染器本身无法执行很多操作: 所有高权限访问都必须通过浏览器的进程进行路由。 攻击者需要在系统的不同部分找出几个漏洞 从而极大地降低成功赢利的风险。
对 eval()
进行安全沙盒化
通过沙盒和
postMessage
API、
该模型能否成功运用到网络相当简单。碎片
您的应用可以位于沙盒化 iframe
中,并且父文档可以
通过发布消息和监听
响应。这种结构可以确保
尽量减少损害。它还有一个优势就是
创建清晰的集成点,以便您准确了解需要
请谨慎验证输入和输出。我们来看一个玩具示例
只是想看看如何做到这一点
Evalbox 是一款令人兴奋的应用 该方法接受一个字符串并将其评估为 JavaScript。哇,对吧?内容 你终于等了这么久。这是一个相当危险 因为允许执行任意 JavaScript 意味着 来源提供的所有数据都可供抓取我们将降低 Bad ThingsTM 通过确保代码在沙盒内执行 这使得它更加安全我们将从 先从框架的内容开始:
<!-- frame.html -->
<!DOCTYPE html>
<html>
<head>
<title>Evalbox's Frame</title>
<script>
window.addEventListener('message', function (e) {
var mainWindow = e.source;
var result = '';
try {
result = eval(e.data);
} catch (e) {
result = 'eval() threw an exception.';
}
mainWindow.postMessage(result, event.origin);
});
</script>
</head>
</html>
在框架内,我们有一个非常小的文档,仅用于监听消息
(通过挂接到 window
对象的 message
事件),从其父项中移除。
每当父级在 iframe 的内容上执行 postMessage 时,此事件都会触发
触发后,我们就能够获取父级希望我们执行的字符串
。
在处理程序中,我们获取事件的 source
属性,该属性是父级
窗口。完成验证后,我们会使用此信息将
完成。然后,我们来完成繁重的工作:将获得的数据传递到
eval()
。由于禁止的操作,此调用已封装在 try 块中
在沙盒化的 iframe
中经常会生成 DOM 异常;我们将捕捉
并报告友好的错误消息。最后,我们将结果
返回父窗口。整个过程非常简单。
父级同样简单。我们将使用 textarea
创建一个小型界面,
用于代码,使用 button
执行代码。我们将通过frame.html
在沙盒中运行的 iframe
,仅允许执行脚本:
<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
id='sandboxed'
src='frame.html'></iframe>
现在,我们来连接代码以便执行。首先,我们监听来自
iframe
,并alert()
提供给我们的用户。大概是真实的应用
则会执行不太令人生厌的操作:
window.addEventListener('message',
function (e) {
// Sandboxed iframes which lack the 'allow-same-origin'
// header have "null" rather than a valid origin. This means you still
// have to be careful about accepting data via the messaging API you
// create. Check that source, and validate those inputs!
var frame = document.getElementById('sandboxed');
if (e.origin === "null" && e.source === frame.contentWindow)
alert('Result: ' + e.data);
});
接下来,我们将连接一个事件处理脚本来点击 button
。当用户
我们将获取 textarea
的当前内容,并将其传递到
执行框架:
function evaluate() {
var frame = document.getElementById('sandboxed');
var code = document.getElementById('code').value;
// Note that we're sending the message to "*", rather than some specific
// origin. Sandboxed iframes which lack the 'allow-same-origin' header
// don't have an origin which you can target: you'll have to send to any
// origin, which might alow some esoteric attacks. Validate your output!
frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);
很简单,对吧?我们创建了一个非常简单的评估 API,可以确信 接受评估的代码无权访问 Cookie 等敏感信息 或 DOM 存储同样,经过评估的代码也无法加载插件、弹出新窗口或 或任何其他令人厌烦的或恶意的活动。
您可以将单体式应用分解成 单一用途组件每种方法都可以封装在一个简单的消息传递 API 中, 就像上面写的那样高权限父窗口可充当 控制器和调度程序将消息发送到 完成工作、监听结果以及 确保每个模块仅提供所需信息。
但请注意,处理加框链接的内容时需要非常小心
与父级同源。如果某网页位于
https://example.com/
使用沙盒为同一来源的另一个网页构建框架
(包含 allow-same-origin 和 allow-scripts 标志),则
通过加框页面与父页面接触,可移除沙盒属性
在沙盒中畅玩
现在,您可以在多种浏览器中使用沙盒功能:Firefox 17+、
IE10+ 和 Chrome 浏览器(当然,
支持表格)。应用 sandbox
属性设为 iframes
,您可以通过添加来向
所显示的内容,而仅授予
才能正常运行。这样,您便有机会降低
提供的内容,
已经实现内容安全
政策。
而且,沙盒也是一种强大的技术,可以降低 攻击者将能够利用您代码中的漏洞。通过将 集成到一组沙盒化服务中,每项服务负责一个 自包含功能的小部分,攻击者将不得不 只会影响同时还要负责其控制器那是 这要困难得多,尤其是当控制器可以大幅降低时, 范围内。如果您满足以下条件,则可以花掉安全相关工作来审核这些代码 你可以让浏览器帮助你完成其余设置
这并不是说沙盒就完全可以解决 确保互联网安全它提供了纵深防御,除非您 您用户的但您还不能依赖浏览器支持来实现所有 (如果您确实控制着用户的客户端 - 企业环境 例如,太棒了!)。总有一天...但现在,沙盒是 以强化您的防御,但这并不是 值得信赖不过,图层的效果还是很棒。建议您利用 一个。
延伸阅读
“HTML5 应用中的特权分离” 一篇有趣的论文涉及一个小型框架的设计, 及其应用到三个现有的 HTML5 应用
与另外两种新 iframe 结合使用时,沙盒可更加灵活 属性:
srcdoc
、 和seamless
。 借助前者,你可以在框架中填充内容 HTTP 请求,后者可将样式融入框架内容中。 目前,两者的浏览器支持都相当糟糕(Chrome 和 WebKit) 每晚)。但将来会是一个很有趣的组合。您可以 例如,通过以下代码对文章进行沙盒评论:<iframe sandbox seamless srcdoc="<p>This is a user's comment! It can't execute script! Hooray for safety!</p>"></iframe>