CSS 侧信道攻击与 :visited 伪类:历史记录嗅探与浏览器防御
大家好,今天我们来深入探讨一个安全领域中比较有趣但也相当重要的课题:CSS 侧信道攻击,特别是利用 :visited 伪类进行历史记录嗅探,以及浏览器针对此类攻击所采取的防御机制。
侧信道攻击:简介与概念
首先,什么是侧信道攻击?与传统的直接攻击(例如缓冲区溢出、SQL 注入等)不同,侧信道攻击并不直接利用目标系统的漏洞,而是通过观察和分析目标系统运行时的物理或逻辑特征来获取敏感信息。这些特征可能包括:
- 时间信息: 执行特定操作所花费的时间。
- 功耗: 设备在执行操作时的功耗变化。
- 电磁辐射: 设备在执行操作时产生的电磁辐射。
- 声音: 设备在执行操作时发出的声音。
- 缓存行为: 处理器缓存的命中率和未命中率。
- 网络流量: 网络数据包的大小和时间。
通过对这些侧信道的分析,攻击者可以推断出目标系统的内部状态,例如密钥、算法、数据等等。侧信道攻击的威胁在于,即使系统本身没有明显的漏洞,攻击者仍然可能通过间接的方式获取敏感信息。
:visited 伪类:历史记录的窗口
现在,让我们聚焦于 CSS 的 :visited 伪类。:visited 伪类允许开发者根据用户是否访问过某个链接来设置该链接的样式。例如:
a:link { color: blue; } /* 未访问过的链接 */
a:visited { color: purple; } /* 访问过的链接 */
这段代码会将未访问过的链接显示为蓝色,访问过的链接显示为紫色。乍一看,这只是一个简单的样式设置功能,但它却为侧信道攻击打开了一扇窗。
为什么 :visited 伪类会成为安全风险?
原因在于,通过 :visited 伪类,攻击者可以探测用户是否访问过特定的网站。如果攻击者能够控制网站上链接的 URL,他们就可以通过 JavaScript 配合 CSS 来构建一个历史记录嗅探器。
历史记录嗅探:攻击原理与实现
历史记录嗅探的原理很简单:
- 攻击者创建一个包含大量链接的网页,这些链接指向他们想要探测的网站。
- 攻击者使用 JavaScript 动态地测量每个链接的样式属性,例如
color或visibility。 - 如果某个链接的样式属性显示为
:visited伪类定义的样式,那么攻击者就可以推断出用户已经访问过该链接指向的网站。
攻击示例:
以下是一个简单的历史记录嗅探示例:
<!DOCTYPE html>
<html>
<head>
<title>History Sniffing Example</title>
<style>
a:link { color: blue; }
a:visited { color: red; }
#result { margin-top: 20px; }
</style>
</head>
<body>
<h1>History Sniffing Example</h1>
<p>Checking if you've visited the following websites:</p>
<a id="google" href="https://www.google.com">Google</a><br>
<a id="facebook" href="https://www.facebook.com">Facebook</a><br>
<a id="example" href="https://www.example.com">Example.com</a><br>
<div id="result"></div>
<script>
function checkHistory() {
const googleLink = document.getElementById('google');
const facebookLink = document.getElementById('facebook');
const exampleLink = document.getElementById('example');
const resultDiv = document.getElementById('result');
let result = "You have visited:<br>";
if (getComputedStyle(googleLink).color === 'rgb(255, 0, 0)') { // Red
result += "Google<br>";
}
if (getComputedStyle(facebookLink).color === 'rgb(255, 0, 0)') { // Red
result += "Facebook<br>";
}
if (getComputedStyle(exampleLink).color === 'rgb(255, 0, 0)') { // Red
result += "Example.com<br>";
}
resultDiv.innerHTML = result;
}
window.onload = checkHistory;
</script>
</body>
</html>
在这个例子中,我们创建了三个链接,分别指向 Google、Facebook 和 Example.com。JavaScript 代码会在页面加载完成后检查每个链接的颜色。如果链接的颜色是红色(:visited 伪类定义的颜色),那么我们就在 result div 中显示该网站的名称。
攻击的复杂性:
虽然上述示例非常简单,但攻击者可以通过一些技巧来提高攻击的隐蔽性和准确性:
- 使用大量的目标 URL: 攻击者可以使用大量的目标 URL 来构建一个更全面的历史记录档案。
- 使用 iframe: 攻击者可以使用 iframe 将历史记录嗅探代码嵌入到其他网站中,从而扩大攻击范围。
- 使用定时器: 攻击者可以使用定时器来定期执行历史记录嗅探,从而跟踪用户的浏览行为。
- 利用其他 CSS 属性: 除了
color之外,攻击者还可以利用其他 CSS 属性,例如visibility、display等,来探测历史记录。
代码示例:使用 visibility 属性
<!DOCTYPE html>
<html>
<head>
<title>History Sniffing Example (Visibility)</title>
<style>
a:link { visibility: visible; }
a:visited { visibility: hidden; }
#result { margin-top: 20px; }
</style>
</head>
<body>
<h1>History Sniffing Example (Visibility)</h1>
<p>Checking if you've visited the following websites:</p>
<a id="google" href="https://www.google.com">Google</a><br>
<a id="facebook" href="https://www.facebook.com">Facebook</a><br>
<a id="example" href="https://www.example.com">Example.com</a><br>
<div id="result"></div>
<script>
function checkHistory() {
const googleLink = document.getElementById('google');
const facebookLink = document.getElementById('facebook');
const exampleLink = document.getElementById('example');
const resultDiv = document.getElementById('result');
let result = "You have visited:<br>";
if (getComputedStyle(googleLink).visibility === 'hidden') {
result += "Google<br>";
}
if (getComputedStyle(facebookLink).visibility === 'hidden') {
result += "Facebook<br>";
}
if (getComputedStyle(exampleLink).visibility === 'hidden') {
result += "Example.com<br>";
}
resultDiv.innerHTML = result;
}
window.onload = checkHistory;
</script>
</body>
</html>
在这个例子中,我们使用 visibility 属性来判断链接是否被访问过。访问过的链接会被设置为 hidden,未访问过的链接会被设置为 visible。
浏览器防御机制:对抗历史记录嗅探
由于历史记录嗅探构成了严重的安全威胁,各大浏览器厂商都采取了各种防御机制来对抗此类攻击。这些防御机制主要包括:
-
样式限制:
早期的防御机制是限制
:visited伪类可以修改的 CSS 属性。浏览器只允许:visited伪类修改颜色相关的属性,例如color、background-color、border-color等。这样可以防止攻击者利用其他 CSS 属性来探测历史记录。局限性:
即使只允许修改颜色相关的属性,攻击者仍然可以利用这些属性来探测历史记录。例如,攻击者可以测量链接的颜色值,或者使用透明度来隐藏链接。
-
时间限制:
一些浏览器会限制 JavaScript 获取
:visited伪类样式的频率。例如,浏览器可能会限制getComputedStyle()函数的调用次数,或者延迟返回样式值。这可以降低攻击者的探测速度。局限性:
攻击者可以通过一些技巧来绕过时间限制。例如,攻击者可以使用
setTimeout()函数来延迟调用getComputedStyle()函数,或者将大量的目标 URL 分散到多个页面中。 -
随机化:
一些浏览器会随机化
:visited伪类的样式。例如,浏览器可能会随机地为访问过的链接设置不同的颜色值。这可以防止攻击者通过比较颜色值来探测历史记录。局限性:
随机化可能会影响网站的正常显示。此外,攻击者仍然可以通过一些统计分析方法来推断用户的历史记录。
-
隐私模式:
隐私模式(例如 Chrome 的隐身模式、Firefox 的隐私浏览模式)会禁用历史记录功能,从而彻底阻止历史记录嗅探。
局限性:
隐私模式只在用户主动启用时才有效。大多数用户仍然使用普通浏览模式,因此仍然受到历史记录嗅探的威胁。
-
限制
getComputedStyle()的使用:一些浏览器限制了 JavaScript 使用
getComputedStyle()来获取:visited样式的能力。或者返回模糊化的结果,使得脚本无法准确判断链接是否被访问过。这是目前最有效的防御手段之一。
-
Storage Access API (SAA)
SAA 主要用于控制第三方网站访问 cookie 和其他存储机制的权限。虽然 SAA 的主要目标不是直接防止
:visited攻击,但它可以间接提供保护。攻击者通常需要使用 cookie 或其他存储机制来追踪用户的行为,并进行更精确的攻击。通过限制第三方网站的存储访问权限,SAA 可以降低攻击者进行历史记录嗅探的有效性。
不同浏览器的防御策略:
| 浏览器 | 主要防御机制 |
|---|---|
| Chrome | 限制 :visited 伪类可以修改的 CSS 属性,限制 getComputedStyle() 的使用,隐私模式,Storage Access API。 |
| Firefox | 限制 :visited 伪类可以修改的 CSS 属性,限制 getComputedStyle() 的使用,隐私模式,增强型跟踪保护 (Enhanced Tracking Protection, ETP) (默认阻止第三方跟踪器,包括可能用于历史记录嗅探的脚本),Storage Access API。 |
| Safari | 限制 :visited 伪类可以修改的 CSS 属性,限制 getComputedStyle() 的使用,隐私模式,智能跟踪预防 (Intelligent Tracking Prevention, ITP) (更积极地限制跨站跟踪,包括可能用于历史记录嗅探的脚本),Storage Access API。 |
| Edge | 限制 :visited 伪类可以修改的 CSS 属性,限制 getComputedStyle() 的使用,隐私模式,跟踪预防(类似于 Firefox 的 ETP),Storage Access API。 |
开发者的应对策略:最佳实践
虽然浏览器厂商已经采取了各种防御机制,但开发者仍然需要采取一些措施来保护用户的隐私:
-
避免使用
:visited伪类:如果不需要根据用户是否访问过某个链接来设置样式,尽量避免使用
:visited伪类。 -
谨慎处理第三方内容:
如果网站允许用户上传内容或嵌入第三方内容(例如广告、评论),需要对这些内容进行严格的审查,防止其中包含历史记录嗅探代码。
-
实施内容安全策略 (CSP):
CSP 可以限制网站可以加载的资源,从而防止攻击者注入恶意脚本。
例如,可以通过以下 CSP 规则来禁止加载外部脚本:
<meta http-equiv="Content-Security-Policy" content="script-src 'self'"> -
教育用户:
向用户普及隐私保护意识,建议用户使用隐私模式浏览敏感网站。
-
使用 Subresource Integrity (SRI):
SRI 允许浏览器验证从 CDN 或其他第三方来源加载的文件的完整性。通过使用 SRI,您可以确保加载的 JavaScript 或 CSS 文件没有被篡改,从而防止攻击者注入恶意代码,即使 CDN 被攻破。
例如:
<link rel="stylesheet" href="https://example.com/style.css" integrity="sha384-oqVuAfW9G955QVhcplwhVtS6oG39iwcL2s4h0JM69/WwbgzcLmhcDQ==" crossorigin="anonymous"> <script src="https://example.com/script.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script> -
定期安全审计:
定期对网站进行安全审计,检查是否存在历史记录嗅探漏洞或其他安全风险。
攻击与防御的持续演进
值得注意的是,侧信道攻击和防御是一个持续演进的过程。攻击者会不断寻找新的攻击方式,而浏览器厂商也会不断改进防御机制。因此,开发者需要密切关注安全领域的最新动态,及时采取相应的措施。
总结:保护用户隐私,开发者责无旁贷
CSS 侧信道攻击,特别是利用 :visited 伪类进行历史记录嗅探,是对用户隐私的严重威胁。浏览器厂商已经采取了多种防御机制,但开发者也需要积极参与,共同保护用户的隐私。通过避免使用 :visited 伪类、谨慎处理第三方内容、实施内容安全策略等措施,我们可以有效地降低历史记录嗅探的风险。记住,保护用户隐私是每个开发者的责任。
更多IT精英技术系列讲座,到智猿学院