Silverlight安全通信机制深度解析:透明插件如何保障浏览器安全

本文通过对比Adobe Flash的getURL漏洞,深入分析Microsoft Silverlight 3 Beta的透明通信机制,揭示其如何严格遵循浏览器安全策略,避免跨域策略绕过问题,并通过HtmlPage类与PopupWindow()方法的实际案例验证其安全性设计。

Hi!我是Manuel Caballero。
我有幸对之前版本的Microsoft Silverlight进行了渗透测试(pen-testing),而在过去三周里,我一直在试用Silverlight 3的Beta版本。我说“有幸”是真心话。研究Silverlight意味着研究一个从安全角度来看诞生时就已经成熟的插件。显然,Silverlight团队投入了大量时间构建Silverlight,以避免其他插件过去存在的许多安全问题。这一点毋庸置疑。

当你阅读有关Silverlight安全性的内容时,无疑会遇到“透明性”(Transparency)这一术语,指的是透明模型(这一概念借鉴自.NET Framework 2.0)。然而,我想借用“透明性”这一术语,并从完全不同的角度来使用它。

我一直在研究Silverlight与其宿主之间的通信,而“透明”这个词几乎在每次尝试时都浮现在我的脑海中。让我解释一下原因。在通信方面,Silverlight似乎与其宿主一样安全。我的渗透测试是使用Windows Internet Explorer 8作为宿主,插件是Silverlight 3 Beta。

从通信的角度来看,在页面上拥有Silverlight插件就像拥有另一段JavaScript代码。使用Silverlight执行的JS代码有多安全?它与直接从浏览器执行JS一样安全。这就是为什么我说Silverlight与其宿主之间的通信是透明的——因为它就像Silverlight什么都没做一样,只是像宿主一样执行JS代码。

即使这看起来很明显,但事实并非如此。让我们以众所周知的插件Adobe Flash为例。Flash有多种与宿主通信的方式(ExternalInterface.call、fsCommand、Get/SetVariable等),但像getURL这样的传统方法仍然存在,随时可用。现在,像getURL这样的方法有什么问题?getURL是一种在窗口、框架或IFRAME中加载URL的方法。历史问题在于其实现(用于加载URL的二进制代码)与浏览器使用的实现是平行的。这意味着Flash绕过了浏览器加载URL的安全实现,使宿主施加的限制无效。例如,可以使用getURL绕过弹出窗口阻止程序,因为打开新窗口的方法不完全由宿主控制。换句话说,使用Flash的getURL方法完全欺骗了浏览器,绕过了许多限制。以下是一个简单的跨域示例:

1
getURL("javascript:alert(document.cookie)", "IFRAME_NAME", "POST");

getURL的JavaScript代码在IFRAME内部执行,完全没有域限制。浏览器的跨域策略被完全绕过,允许恶意站点访问外部(善意)站点的完整DOM。

这个跨域版本从Flash 7.x到Flash 9.0.115.0都非常有效。Adobe在修复一个具有相同根本原因的不同问题时修复了它,但这个bug早在Flash 7之前就存在:一个简单的变体(用GET代替POST)在旧版Flash中曾经有效。

简而言之,插件引入了一种在浏览器中执行JS的方式,这种方式独立于浏览器的安全策略。实际上,它绕过了浏览器施加的许多限制。那么,为什么插件需要引入新的加载URL的方式,而宿主本身已经有安全的方法来做到这一点?

如果getURL只调用浏览器原始的加载URL方法,而不是本地方法,那会好得多。在我看来,那将是透明的——就像根本不存在一样。

现在,Silverlight有什么不同?区别很明显:Silverlight直接调用安全的浏览器方法,除了一些罕见的情况,它在调用这些相同的安全浏览器方法之前会解析数据。从通信的角度来看,Silverlight只是浏览器的仆人。如果浏览器是安全的,Silverlight也是安全的。

让我们看两个清晰的例子:

示例1
Silverlight访问宿主的一种方式是通过HtmlPage类。例如,Silverlight中的HtmlPage.Window.Alert(“Hello”)将调用浏览器中window对象的alert方法。它不会调用User32!MessageBox或.NET Framework的MessageBox.Show。它实际上会调用浏览器的alert方法。事实上,如果我们在宿主中覆盖原始的window.alert(),Silverlight将调用它的新/覆盖版本。这很好。这意味着Silverlight正在做它必须做的事情——调用alert方法,仅此而已。没有检查,没有重新发明轮子,没有奇怪的东西。只是调用浏览器中已经编码的alert方法!

因此,如果我们使用JavaScript覆盖浏览器中的alert方法:

1
window.alert = function(strText){document.write(strText);}

然后通过Silverlight调用alert方法:

1
HtmlPage.Window.Alert("Hello");

结果将在宿主窗口中执行document.write(“Hello”)。这正是从“透明”插件所期望的。其他方法如Eval、Prompt等也是如此。

示例2
现在,为了看看这能走多远,让我们考虑一个宿主和Silverlight之间的通信被关闭的场景。我们可以通过将OBJECT标签中的enableHtmlAccess参数设置为false来实现:

1
2
3
<object data="data:application/x-silverlight," […]>
<param name="enableHtmlAccess" value="false" />
</object>

想象一下Silverlight在一个横幅广告中。因此,当用户点击它时,广告商希望打开一个弹出窗口,其中包含他们的主页。由于通信被关闭,您无法调用宿主本地方法。然而,Silverlight开发人员添加了一种简单的方法来允许插件打开弹出窗口(除了HyperlinkButton,那是另一回事)。它是HtmlPage.PopupWindow(),仅当AllowHtmlPopupWindow参数设置为true或允许html访问时有效。

1
2
3
4
<object data="data:application/x-silverlight," […]>
<param name="AllowHtmlPopupWindow" value="true" />
<param name="enableHtmlAccess" value="false" />
</object>

从现在开始,Silverlight对象可以调用并成功使用HtmlPage.PopupWindow()方法打开弹出窗口。

在这种情况下,Silverlight团队似乎有理由使用本地方法(就像Flash中的getURL)打开弹出窗口,绕过浏览器限制。我的意思是,站在编写HtmlPage.PopupWindow方法的人的角度。我敢肯定,忘记安全一秒钟,“只是用任何方便的方法打开那个该死的弹出窗口”的想法曾出现在他的脑海中。然而,Silverlight团队再次选择通过使用浏览器方法来减少在宿主中引入新安全漏洞的机会。

自己检查一下。让我们通过JS覆盖window对象的open方法:

1
window.open = function(a, b, c){alert(a + ", " + b + ", " + c);}

现在禁止宿主和Silverlight之间的通信,将AllowHtmlPopupWindow设置为true,然后从Silverlight调用PopupWindow():

1
HtmlPage.PopupWindow(new Uri("http://www.cracking.com.ar"), tbHyperLinkTarget.Text, new HtmlPopupWindowOptions());

享受alert而不是弹出窗口吧。

因此,即使所有赌注都关闭了,Silverlight也遵守规则,只做必要的工作来完成工作。在我看来,这非常好!

Manuel Caballero。
独立安全研究员 - http://www.cracking.com.ar

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计