侧信道嗅探浏览器历史

admin 2022年5月30日01:13:30评论30 views字数 14138阅读47分7秒阅读模式

侧信道嗅探浏览器历史

笔记作者:ZeddYu@SecQuan

文章小编: ourren@SecQuan

原文作者:Michael Smith, Craig Disselkoen, Shravan Narayan, Fraser Brown, Deian Stefan

原文标题:Browser history re:visited

原文链接:https://www.usenix.org/system/files/conference/woot18/woot18-paper-smith.pdf

TL;DR

本文发表在 WOOT'18: Proceedings of the 12th USENIX Conference on Offensive Technologies

在本文中,作者介绍了通过一些侧信道形式嗅探用户是否访问过某网站的方法,尽管发的会不是很出名,但是个人认为整个思路还是很有意思的,也学到了一些东西,所以写了一下比较长篇的笔记介绍。因为自己不经常写笔记,所以写的有点像翻译文章,今后如果时间精力允许会更简洁一些。(但是确实我觉得文章思路很棒的说

Introduction

作者在本篇中提出了四个新的嗅探用户访问历史 ( history sniffing ) 的攻击,主要分两类:针对访问过的链接的攻击形式 ( visited-link attack ) 和基于缓存的攻击 ( cache-based attack ) 。这些攻击通过滥用新的浏览器功能(例如, CSS 绘画 API 和 JavaScript 字节码缓存等)来实现,主要利用的是浏览器在处理跨源 URL 数据时忽略了隐私问题。作者评估了针对以下浏览器的攻击,四个主要的浏览器(Chrome、Firefox、Edge 和 IE )以及 几个注重安全的浏览器(ChromeZero, Brave, FuzzyFox, DeterFox, 和Tor浏览器)。作者的两个攻击对除 Tor 浏览器以外的所有浏览器都是有效的,而另外两个攻击则针对 Chrome 浏览器的特定功能,而另外两个攻击则针对 Chromium 衍生的浏览器。此外,作者的一个针对访问链接的攻击(CVE- 2018-6137)可以以每秒 6,000 个 URL 的速度窃取用户的历史记录。作者希望这项工作将推进浏览器供应商进一步重新考虑处理隐私敏感数据的浏览器功能设计。

在现代社会中,通过一个人的浏览器历史记录可以分析出这个人的一些特征,比如年龄、性别、地点等,所以浏览器历史记录作为个人隐私也显得极为重要;虽然浏览器没有为 JavaScript 提供直接的手段来读出用户的历史,但是在实际中,浏览器仍然允许网页开发者对历史数据进行有限的操作,例如使用 CSS 当中的 :visited 和 :link 选择器,开发者可以根据一个链接的目标 URL 是否出现在用户的浏览历史中而有条件地设计它,而攻击者更可以通过类似的方式获取到历史记录。所以浏览器必须考虑到各种滥用,比如利用 CSS 选择器作为侧面渠道,"嗅探 "一个URL的访问状态。

早在2002年,攻击者就发现了可以通过检测 :visited 选择器是否与给定的链接元素相匹配的方法,通过将链接的目的地指向感兴趣的URL,他们可以泄露受害者是否访问过该 URL ;攻击者发现他们可以滥用不同的浏览器功能,如 MozAfterPaint 事件或 requestAnimationFrameAPI,来窃取相同的数据,或者可以使用 :visited 选择器来欺骗他们的受害者,让他们泄露信息;攻击者还可以通过基于浏览器对嵌入式资源(如图像或脚本)的缓存的时机渠道来泄露历史信息。虽然浏览器在发现漏洞时很及时地修补了漏洞,但是他们急于支持新的功能和新的 API ,以适应新的应用类别,从视频游戏到物联网到虚拟和增强现实。当然,为了确保新的应用程序以合理的性能运行,浏览器供应商也不断增加新的缓存和优化,这种日益复杂的功能引入了更多可能泄露历史数据的接口,例如从 CSS 绘画 API 到 JavaScript 字节码缓存。

Background

浏览器对其用户访问的 URL 进行跟踪,以便(1)帮助这些用户识别他们已经访问过的网站(例如,通过用不同的颜色标记熟悉的链接),以及(2)通过缓存资源以避免网络请求来加快浏览速度。不幸的是,攻击者可以利用这种保存状态来了解用户的私人浏览习惯。这种攻击主要有两类: Visited-link attacks 以及 Browser-cache attacks

Visited-link Attacks

通过 JavaScript ,开发者可以通过调用 getComputedStyle 方法来查询任何元素的计算样式属性,该方法返回的数据如 {color: "purple"} 。浏览器还允许使用 :visited 选择器为被访问的链接设置任意的样式,例如下面是一个例子,没访问过的链接是蓝色,访问过的链接是紫色。

/* Default link color to blue: */
a { color: blue; }
/* Turn visited links purple: */
a:visited { color: purple; }

根据链接的被访问状态,攻击者可以用 CSS 改变其外观,然后通过 JavaScript 观察。

为了应对现实世界中的这种历史嗅探攻击,主要的浏览器采取了一对缓解措施。首先,他们通过限制 getComputedStyle 来解决显性泄漏问题,即谎报链接的计算样式:该方法现在总是返回链接的未访问版本的样式。其次,他们通过限制 :visited 的链接样式的颜色来解决隐性泄露问题,这本应该是 JavaScript 无法获取到的,所以浏览器应尽可能地缓存链接的计算样式,而不是重新计算它们,以努力避免时间攻击。除此之外,攻击者仍然可以通过各式各样的方法来获取用户历史信息,例如使用互动任务(如验证码)来欺骗用户披露历史信息,从网络摄像头图像的屏幕反射中推断出链接的颜色等等。

Browser-cache Attacks

浏览器依靠许多层缓存来加快网络应用;通过缓存HTML文档或视频等资源,浏览器可以避免在用户第二次访问页面时重新获取该资源的开销。大多数浏览器只使用资源的 URL 来索引缓存条目,而不考虑嵌入资源的页面的来源。这使得网络攻击者在 https://evil.com ,对 https://fb.com 进行跨源请求时,可以通过测量该请求的持续时间来了解用户是否访问过该网站。如果来自 https://fb.com 的资源已经在缓存中,则请求会更快;如果浏览器必须通过网络获取,则会更慢,如果目标资源足够大,则会更慢。还有一些类似的浏览器功能,例如 App Cache 、 Service Worker 、 Quota management API 等,都可以被用来进行类似的攻击。

Visited-link attacks on history

通过本章节,作者主要介绍了了三种针对被访问链接的相关攻击 ( Visited-link Attacks ),这些攻击向攻击者透露了用户的浏览历史。这些 "重绘 "攻击分别利用了现代浏览器处理被访问链接的一个基本漏洞:通过强迫浏览器根据链接的被访问状态重绘,并测量重绘事件发生的时间,攻击者可以了解链接所指向的 URL 是否被访问过。攻击者可以检测任意的、精确的URL的访问状态,包括路径信息。此外,他们可以在不被受害者察觉的情况下做到这一点。

Abusing the CSS Paint API

作者展示了攻击者如何使用 CSS Paint API 嗅探历史数据。2018 年推出的 CSS Paint API 可以让网站可以 Hook 浏览器的渲染进程 ( rendering pipline ),自己绘制 HTML 元素的一部分,例如,用重复的棋盘图案填充网页的背景,以适应任何窗口大小和显示分辨率。通过检测这些 Hook 何时被调用,攻击者可以观察到浏览器何时在页面上重新绘制一个链接元素。

基于这个理论,在目标 URL 之间切换链接会导致链接在其访问状态发生变化时被重新绘制,因此攻击者可以推断出这些 URL 是否被访问过。可以通过 CSS Paint API 来观察一个URL是否被访问,方法是:

  1. 制作一个链接元素,只有当其相关的 URL 被访问时才会被重新绘制
  2. 使用一个时间通道(通过浏览器操作的时间泄露信息)来确定是否发生了重新绘制。假设一个攻击者想确定受害者是否访问过 https://ashleymadison.com ,首先,攻击者选择一个他们知道受害者没有访问过的假链接(例如,https://dummy.com;或一个随机生成的URL);然后,攻击者创建一个指向假目标的链接元素,并将链接的背景图像设置为由画图脚本渲染的图像。
<a id="target" href="https://dummy.com">link</a>
<style>
#target {background-imagepaint(myEvilPainter);}
</style>

当浏览器最初绘制链接时,它将调用 myEvilPainter 作为回调。

之后,在一个正常的(非画图)脚本中,攻击者将链接的目的地切换为实际的目标 URL (不改变显示的链接文本): target.href = "https://ashleymadison.com"。如果攻击者随机的、已知未访问的 URL 和目标 URL 都没有出现在受害者的历史记录中,那么该链接的访问状态就会开始为 false ,并且在切换链接 href 后保持这种状态。然而,如果目标 URL 确实出现在受害者的历史记录中,那么链接的 visited 属性就会变为 true ,从而导致链接元素的无效化,迫使浏览器重新绘画链接,从而第二次调用 myEvilPainter 的绘画回调。计算对绘画回调可以告诉攻击者目标 URL 是否被受害者访问过:两次调用表示已访问过,一次调用表示未访问过。

然而,计算这些调用是困难的,因为 paintlet 运行在自己独立的上下文中,很着很多限制:它们不能进行网络请求,不能与其他脚本通信,也不能使用大多数其他 JavaScript API 。此外,浏览器确保它们绘制的像素不能通过 JavaScript 读回,甚至阻止 paintlet 在多次执行中保存状态。尽管有这些限制,攻击者还是可以使用事件循环计时通道来检测浏览器的重新绘制。具体来说,在绘画回调中,可以引入一个运行 20 毫秒的循环,并阻止 JavaScript 事件循环。由于事件循环是共享的,在页面中运行的代码可以直接观察到这个

var start = performance.now();
// ... change link URL & block on paintlet's paint
// callback ...
var delta = performance.now() - start;
if (delta > threshold) {
 alert('Victim visited Ashley Madison!');
}

较长的 delta 表明,从已知未访问的网址到目标待检测网址的变化引起了重新绘制,这反过来意味着受害者访问了目标网址。并且,重新绘制并不是即时的,其会被加入到浏览器渲染 "下一帧 "的队列当中,进而被处理。由于今天的浏览器以每秒 60 帧的速度渲染网页,这就给重绘的速度设定了一个上限,因此,意味着该攻击最理想情况下,最多每秒泄露 60 个 URL 。

接下来作者介绍了如何批量去检测该类型,使用多个链接元素,每个元素都指向不同的目标URL。整个流程分为两个阶段:

  1. 记录阶段,使用一个 paintlet 来扫描目标 URL ,并通过使用 registerPaint 函数来存储它们的访问状态。
  2. 读取阶段,使用另一个 paintlet 来 "读取 "访问状态位,并通过 CSS 隐蔽通道将它们传达给一个正常的、非 paintlet 的攻击者脚本。

在记录阶段,攻击者首先为每个目标 URL 生成一个独特的标识符串。与我们之前的攻击一样,攻击者然后设置每个链接元素的背景,由 paintlet 渲染。但是,在这次攻击中,绘画回调不需要阻止事件循环,而是使用 registerPaint 将 paintlet 与一个新的标识符联系起来:旧的标识符后缀为 _visited

paint (ctx, geometry, properties) {
 // Get identifier (e.g., ashleyMadison):
 var iden = properties.get('font-family');
 // Associate this paintlet with tweaked
 // identifier (e.g., ashleyMadison_visited):
 registerPaint(`${iden}_visited`, ...);
}

通过将链接的 font-family 样式设置为 URL 标识符,攻击者可以向绘画 callback 传达重新绘画是代表哪个URL运行的,在通过他们的链接元素集给所有目标 URL 批次标识后,攻击者创建一个新的 paintlet 来检查,对每个可能的标识符调用 registerPaint ,如果标识符已经被注册了(在记录阶段),registerPaint 会抛出一个重复调用的异常, paintlet 会捕捉到这些异常,再为每个已经注册的标识符注册一个新的唯一标识符。

try {
 // Try to associate identifier
 // (e.g., ashleyMadison_visited):
 registerPaint(iden, ...);
catch (e) {
 // Create new identifier from the old
 // (e.g., ashleyMadison_exfiltrate):
 var newIden = iden.replace('_visited''_exfiltrate');
 // Associate new identifier with paintlet:
 registerPaint(newIden, ...);
}

接着,攻击者使用 CSS Paint API 实现的一个功能来检测这些新的注册标识符,对每个可能的 *_exfiltrate 标识符都增加一个样式,如:

#ashleyMadison_element::after
contentpaint(ashleyMadison_exfiltrate); }

这段 CSS 代码选择了标识符为 ashleyMadison_element 的 HTML 元素,并插入了一个新的图像元素作为其最后一个子元素。为了绘制这个图像,内容规则规定,浏览器应该调用注册为 ashleyMadison_exfiltrate 标识符的 paintlet 的回调。如果这个 painttlet 标识符刚刚被注册,那么浏览器就会为子图像元素计算一个大的宽度值;否则,如果 ashleyMadison_exfiltrate 不是刚刚注册的,或者根本就没有注册,那么图像元素的宽度就会变小,这样攻击者可以通过 *_element 元素进行循环,并检查它们的宽度:一个大的宽度对应于一个用户访问过的 URL 。

作者提出的批量检测效率在受害者不知情的情况下可以以每秒 3000 个 URL 的速度探测用户的浏览历史,也就是说,可以在 30-40 秒内扫描 Alexa Internet 的前10万个网站列表,整个流程都处于后台应用,对页面没有可见的影响,也不需要受害者的互动。Chrome 浏览器是唯一实现了 CSS Paint API 的浏览器,所以它也是唯一受此攻击影响的浏览器;作者在 Windows、macOS 和 Ubuntu Linux 下的 Chrome 66.0.3359.117 上成功进行了攻击。作为回应,谷歌将 CVE-2018-6137 分配给作者的报告,并奖励了 2000 美元,他们为 Chrome 67 发布的补丁包括一个临时修复,禁用了链接元素及其子元素上的 CSS Paint API 。

Abusing CSS 3D transforms

作者下一个攻击是利用 CSS 的 3D 变换,开发者可以用它在三维空间中对 HTML 元素进行平移、旋转、投影,甚至是动画化。攻击者将这些三维变换堆叠在其他 CSS 效果之上,通过创建一个链接元素,使受害者的浏览器难以绘制。然后,攻击者在两个不同的目标 URL 之间反复切换链接元素,并使用 :visited 选择器迫使浏览器在链接改变访问状态时完成耗时巨大的重绘操作。通过这些操作进而泄露受害者是否访问过目标 URL ,攻击者通过 JavaScript 监控页面的渲染性能来获取这些信息。

如同前面所提到的一样,检测受害者是否访问过目标连接,攻击者首先创建一个指向已知未访问过的假网址的链接。然后,攻击者利用 CSS 三维变换和其他后处理效果来增加浏览器重新绘制链接时的负担。经过作者的尝试,最终得出了一个比较好的实验设置如下,主要包括过滤、阴影和轮廓样式:

#target {
 transformperspective(100pxrotateY(37deg);
 filter:
  contrast(200%)
  drop-shadow(16px 16px 10px #fefefe)
  saturate(200%);
 text-shadow16px 16px 10px #fefffe;
 outline-width24px;
 font-size2pxtext-align: center;
 display: inline-block;
 color: white;
 background-color: white;
 outline-color: white;
}

在作者的攻击实现中,还用一长串随机的中文字符来填充链接的显示文本。

属性的具体组合并不重要,重要的是攻击者通过分层的计算密集型效果使浏览器难以呈现该元素。攻击者想迫使受害者的浏览器在链接的访问状态改变时重新进行这些计算,现代浏览器在最初绘制链接时,会进行一次计算,尽管速度相对较慢,然后重新使用渲染的结果,除非链接的计算样式发生变化。在这种情况下,改变链接的颜色意味着浏览器必须重做攻击者在链接元素上应用的所有昂贵的转换和后处理效果。因此,攻击者可以为该链接写了一个 :visited 样式,为其visited 状态为真时指定一组不同的颜色值。

a:visited {
 color#feffff;
 background-color#fffeff;
 outline-color#fffffe;
}

接下来,攻击者通过 JavaScript 将链接的目标 URL 从 https://dummy.com 已知的、随机得到的未访问链接切换到https://ashleymadison.com 目标链接,当受害者访问过 https://ashleymadison.com 时,改变链接的 href 后,它的访问状态从假变成了真,此时颜色也会发生变化;结果,受害者的浏览器必须用新的颜色从头开始绘制该链接。这里特殊的颜色选择使受害者在白色背景下看不到链接,而从白色到接近白色的变化也同样难以察觉。这样一来切换其访问状态会导致受害者的浏览器进行一次代价昂贵的计算,在完成计算的过程中耽误页面的渲染周期。反复该过程,在两个 URL 之间来回切换,使包含页面的性能明显下降(但由于现代浏览器的并行、多进程架构,浏览器的其他部分或滚动的性能不会下降)。如果攻击者测量到这种性能下降,他们就会知道他们的受害者是一个该网站的用户。

有许多方法可以从 JavaScript 中完成这一工作。可以选择使用 requestAnimationFrame API,这是一个旨在允许 JavaScript 代码驱动流体动画的API。当开发者向这个函数传递回调时,浏览器会在为页面绘制下一帧之前调用该回调。浏览器的目标是每秒调用回调约 60 次(与页面的整体帧率相同),但如果页面的绘制性能下降,这个值就会略小。攻击者可以利用这一点,进行两次测量,每次都是在一个固定的时间窗口。首先,攻击者可以在两个不同的已知未访问的假网址(例如,https://dummy.com 和 https://dummy2.com )之间来回切换,通过循环注册 requestAnimationFrame 的回调并记录浏览器调用的次数来多次收集帧数,以此得到平均帧数 c  。然后,使用同样的程序,攻击者在目标URL https://ashleymadison.com 和已知未访问的 URL 之间来回切换时进行实验测量,得到平均帧数 e 。对比两次的值,如果 e 明显低于 c ,则反映了攻击者第二次测试的绘画性能下降,表明受害者已经访问了 https://ashleymadison.com 。

作者发现该攻击在最新版本的 Chrome、Firefox、Edge 和 Internet Explorer、Windows、macOS 和 Ubuntu Linux 中都是有效的。

Abusing fill-coloring of SVGs

此节与上述攻击类似,只不过使用了更为复杂的 SVG 图像应用,这样的颜色更新对渲染引擎的代价来说是非常昂贵的。

攻击者在一个链接元素中嵌入一个复杂的SVG图像,并在 :visited 选择器下设置一系列 CSS 填充规则,根据链接的访问状态为图像的组件指定不同的颜色值。

<a href="https://dummy.com">
 <svg xmlns="http://www.w3.org/2000/svg">
 ... embedded SVG image data (verbose XML) ...
 </svg>
</a>

在作者的攻击中,我们构建了一个渲染成本极高的 SVG 图像:该图像由许多(7,000多个)复杂的路径元素、充满色彩的多边形多层排列而成。接下来,攻击者针对他们的 SVG 图像设置了一系列的 CSS 填充规则,然后使用包含链接的 :visited 选择器设置另一个样式,当链接被访问过,也就是 visited 状态为真时应用一种颜色模式,否则就应用另一种样式。

a svg * {fill#ffffff;}
a svg *:nth-child(3n+1) {fill#fffffe;}
a svg *:nth-child(3n+2) {fill#fffeff;}
a:visited svg * {fill#feffff;}
a:visited svg *:nth-child(3n+1) {fill#fefffe;}
a:visited svg *:nth-child(3n+2) {fill#fefeff;}

颜色值的选择是为了使 SVG 图像在白色背景上不可见,并且使两种颜色模式之间的交换对受害者来说是不可见的。攻击的其余部分与上文的 3D 旋转完全相同:攻击者使用 JavaScript 快速切换链接的目标 URL 或 :visited 样式规则的颜色值,以便在他们选择的目标URL(例如 https://ashleymadison.com )被用户访问的情况下,强制进行许多昂贵的重新绘制操作。同时,攻击者监控页面的整体渲染性能(例如使用 requestAnimationFrame),并将其与控制测量进行比较,以推断出URL的访问状态。

这种攻击对 Chrome、Firefox、Edge 和 Internet Explorer 都很成功,可以在 100ms 的测量周期内进行访问判断(通常是 2 次回调调用与 5 次回调调用)。随着时间窗口的增加,该差距越来越大(例如,在 1000ms 时, 9 次对比 41 次)。在这里,攻击者也通过选择与页面背景相融合的颜色值,向受害者隐藏了攻击中使用的元素。

Bytecode-cache attacks on history

在不同来源之间共享资源的浏览器优化(例如,缓存)可能会让攻击者探测这些资源,以寻找不同来源的页面留下的痕迹。作者检查了 Chrome 的 JavaScript 字节码缓存(2015年添加),这个缓存保留了 JavaScript 引擎在编译和运行脚本时产生的字节码,如果以后需要再次执行该脚本, JavaScript 引擎可以使用缓存的字节码而不是重新编译该脚本。通过探测字节码缓存,攻击者可以可靠地确定受害者的浏览器以前是否执行过特定的脚本文件,从而推断出受害者的浏览历史细节。事实上,攻击者甚至可以在受害者重启浏览器或机器后检测到过去的脚本执行情况,因为 Chrome 浏览器会将其字节码缓存持续到磁盘上。

假设一个开发者在他们的网站上嵌入了脚本 foo.js ,当用户访问该开发者的网站时,他们的浏览器会下载 foo.js ,但浏览器不会立即开始执行它。浏览器必须首先解析 foo.js 中的 JavaScript 代码(因为脚本是以源代码形式发布的),然后将其编译为适合驱动 JavaScript 引擎的字节码。由于 JavaScript 灵活的语法和语义,这个最初的 "启动" ( boot up ) 阶段耗费了大量的时间。此外,如果开发者在其网站的多个页面上嵌入 foo.js ,性能成本会越来越大,每次页面加载,浏览器都要重复同样的工作来启动 foo.js 。所以 Chrome 浏览器通过其"字节码缓存"优化避免了这种重复工作,该优化针对的是重复执行的、超过预定义大小阈值的脚本。如果 foo.js 符合这种描述,那么在第三次执行该脚本时, Chrome 的 JavaScript 引擎会将生成的字节码存放在以 foo.js 的 URL 为关键的磁盘缓存条目中。在随后的执行中, Chrome 会跳过通常的启动阶段,从缓存中读取字节码。

由于 Chrome 浏览器在不同来源的页面之间共享每个脚本的字节码缓存条目,攻击者可以通过测量 Chrome 浏览器启动特定脚本的时间来推断历史信息。举个例子,攻击者想知道他们的受害者是否访问过https://ashleymadison.com。

。为了可靠地实现这一目标,攻击者必须精确测量两个时间点。(1) 浏览器完成下载脚本的时间;(2) 脚本开始运行的时间;这两个时间点是编译步骤的起点。攻击者必须明确地避免测量浏览器在编译前下载foo.js所花费的时间和编译后执行的时间--这些数字根据许多因素(例如,受害者的网络连接)而变化,因此会引入足够的噪音来掩盖相关的时间信号。为了追踪第1点--当脚本完全下载时--攻击者可以使用资源计时API,它为这个事件提供了一个时间戳[39]。对于第2点--当脚本开始运行时--浏览器没有提供直接的测量手段。然而,攻击者可以通过测量脚本首次设置全局变量的时间来估算脚本的启动时间

  1. 攻击者选择一个由其主页嵌入的脚本,例如,https://ashleymadison.com/foo.js 。所选的脚本要足够大,以至于之前对该网站的访问会导致受害者的浏览器为该脚本生成一个字节码缓存条目。
  2. 然后,攻击者在他们自己的页面中 ( https://attacker.com ) 隐蔽地嵌入一个指向该URL的脚本标签,例如 <script src="https://ashleymadison.com/foo.js"></script>
  3. 当受害者访问 https://attacker.com ,Chrome会下载、编译并执行 foo.js ,如果能在字节码缓存中找到 https://ashleymadison.com/foo.js ,浏览器会跳过编译的步骤。在这种情况下, foo.js 从下载到运行的时间大大少于其他情况(大约几十毫秒;见下图)
  4. 通过测量这个时间差,攻击者可以推断出受害者是否访问了 https://ashleymadison.com 。
侧信道嗅探浏览器历史

每次执行雅虎主页中的 JS 脚本所用的启动时间。在第三次执行该脚本后 Chrome 的 JavaScript 引擎会创建一个缓存条目。显示的时间是10次试验的平均数。

因此,在实施攻击之前,攻击者必须确定 foo.js 设置的第一个全局变量的名称。为此,攻击者记录了 JavaScript 全局范围内的所有变量列表,即窗口对象。接下来,他们注入一个指向 foo.js 的脚本标签,指示浏览器(1)加载和执行目标脚本,然后(2)回调到他们自己的代码,即 loadCallback 函数。在 loadCallback 函数中,攻击者生成了一个新的全局变量列表,并将这个列表与 oldGlobals 进行比较,以确定 foo.js 设置的第一个全局变量的名称。

var oldGlobals = Object.keys(window);

var scriptTag = document.createElement('script');
scriptTag.async = false;
scriptTag.src = 'https://ashleymadison.com/foo.js';
scriptTag.addEventListener('load', loadCallback);
document.head.appendChild(scriptTag);

在攻击阶段,攻击者定义了一个 setter 函数,只要 foo.js 设置了这个全局变量,就会调用该函数。这个函数只是在第一次调用时记录当前时间,脚本开始运行的大致时间。攻击者使用这个时间戳和下载时间戳来计算脚本的启动时间,并将其与参考测量值进行比较,以推断脚本字节码是否先前被缓存。如图2所示,字节码缓存可以使启动时间加快 2.5 倍 - 10 倍,使攻击者很容易推断出受害者之前是否访问过目标网站。

作者发现这种攻击对 Windows 、 macOS 和 Linux 版本的 Chrome 浏览器有效。该攻击需要大约 100ms 的时间来检测目标脚本 URL 的字节码缓存命中或未命中,并且可以多次并行地对目标列表的缓存进行批量查询。攻击者可以通过更聪明地选择目标脚本来了解受害者的更多信息,例如,测试那些只在网站的登录页面才会加载的脚本。

在自动扫描中,我们确认在前 500 个网站中的 372 个(74%)存在一个合适的目标脚本——在压缩前大小为 100KB 或更大。作者认为这是最保守估计的结果,因为他们只扫描每个网站的HTML源静态嵌入的脚本文件。出于性能方面的考虑,一些网站会在初始页面加载后动态地注入它们的脚本;尽管字节码缓存条目仍会为这些脚本生成,并且攻击不需要修改就能发挥作用,但他们的扫描工具还不能检测到动态注入的脚本。此外,剩下的大约 32 个被检测为不可攻击的网站要么是不提供网页服务的 CDN 域名,要么是有证书错误,无法测试。

与对其他网络资源类型(如图像)的缓存定时攻击相比,这种攻击更实用,因为它消除了一些干扰因素(如来自网络的变化)。此外,产生可检测的时间差异所需的文件大小阈值要小得多,使攻击者能够针对广泛的网站测试。与大多数历史侦查缓存计时攻击一样,这种攻击是破坏性的:查询字节码缓存条目的过程中,如果该条目不存在,就会强制创建该条目。这意味着该攻击不能针对同一受害者的同一脚本 URL 重复进行。

Consequences and defense

作者在三种操作系统( Windows、macOS 和 Ubuntu Linux )上的 Chrome 和 Firefox 中进行了测试,并在 Edge 和 Internet Explorer 中进行了测试。本文介绍的这两类攻击( CSS Paint API 攻击和字节码缓存攻击),只影响 Chrome ,因为它们的目标是其他浏览器尚未实现的功能。另外两种攻击,涉及 CSS 三维变换和 SVG 填充着色使用了更多的传统功能,并且证明在所有这四种浏览器中都有效。最后,作为比较,作者测试了 Paul Stone 的 visited-link 攻击的两个独立实现,发现它只在 macOS 和 Linux 的 Firefox 中成功,如前所述。除了测试主要浏览器的原始版本外,我们还评估了我们对这些启用了额外隐私功能的浏览器的攻击,以及对几个面向隐私的浏览器的攻击:

  • Chrome with Site Isolation. 站点隔离 ( Site Isolation ) 将每个站点的数据限制在独立的进程中,但没有对 :visited 选择器或字节码缓存施加任何限制,它们保持共享。因此,这一特性并不能阻止本文介绍的攻击。
  • ChromeZero research extension. ChromeZero 试图通过限制某些 JavaScript APIs 来阻挠攻击者,但是由于本文的攻击并不依赖这些API,因此即使扩展程序处于最高保护模式,它们仍然可以发挥作用。
  • Brave. Brave 是一个面向安全和隐私的浏览器,但由于 Brave 是建立在 Chromium 之上,作者发现它容易受到与 Chrome 相同的攻击。
  • Firefox with visited links disabled. 禁用 Firefox 的 layout.css.visited links enabled 配置标志本应可以完全消除 Visited-links Attacks 的影响,但事实并非如此:禁用该标记既不能阻止本文的 Visited-link Attacks ,也不能阻止 Paul Stone 之前提出的攻击,作者也向 Mozilla 报告了这个错误。
  • The Tor Browser. 这个火狐浏览器的发行版没有记录用户历史,因此对我们的攻击是免疫的。
  • The FuzzyFox and DeterFox research browsers. 这两个修改版的火狐浏览器通过减小显性、隐性计时器的精度来规避侧信道;FuzzyFox 还对浏览器事件的安排进行了规范化处理。本文介绍的攻击与其他对 Firefox 的攻击仍然有效,因为它们不依赖于计时器的精度,但 FuzzyFox 会使本文的攻击效率降低 10 倍左右。
侧信道嗅探浏览器历史

Defense

为了解决反复出现的违反同源政策的问题,像 Mozilla 这样的浏览器重构了他们的浏览器架构来加强 SOP 策略。作者认为,他们应该同样地重组浏览器以解决类似的历史嗅探攻击。为此,作者提出了一个涵盖持久性数据的同源性政策:浏览器在存储资源时不应该只使用资源的 URL ,还应该关联代码所代表的页面的来源。例如,任何历史记录都应标明引用页面的来源,每个字节码缓存条目都应标明嵌入相应脚本的页面的来源,等等。因此,在获取存储数据时,浏览器应该检查执行查询的页面的来源,只有在该来源与存储数据相关的 referrer-origin 相同时才会成功。

当然,这种防御会产生一些成本。对于字节码缓存来说,在每个站点上初始加载一个脚本会付出缓存丢失的代价,但随后的页面加载仍然会从缓存中受益。为了解决这个问题,作者设想了一个跨站点资源共享(CORS)的扩展,允许流行的公共资源(例如 jQuery )在站点之间安全共享。其提出的防御措施也部分地打破了网络兼容性,具体来说,CSS 中的 :visited选择器将只准确地表示链接是否被来自同一来源的页面点击过。但是,浏览器之前已经破坏了隐私的兼容性:例如,对最初的 :visited 泄漏的修复改变了 getComputedStyle API 以返回不正确的信息,并破坏了使用 :visited 的现有样式表。

安全学术圈招募队友-ing, 有兴趣加入学术圈的请联系secdr#qq.com

原文始发于微信公众号(安全学术圈):侧信道嗅探浏览器历史

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月30日01:13:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   侧信道嗅探浏览器历史http://cn-sec.com/archives/583226.html

发表评论

匿名网友 填写信息