一提到 CSP 大家可能会想起 XSS
,没错,CSP 诞生之初就是为了防御层出不穷的 XSS 漏洞的(虽然目前看起来效果并不好),不过利用它的特性,也能有其他的用途,比如…… 反运营商劫持。
在中国这个神奇的国度,一款产品用户多了,必然会出现这样的反馈——「你们的网站/APP上怎么总是有色情广告?」。当查来查去最后发现是运营商搞的鬼时该怎么办?去工信部投诉?能否解决另说,怎么找出各个地区的各种违规的运营商就已经让人头大了。于是有人想到了前端防御:维护一个黑名单,当前端检测到有黑名单元素存在页面上时就把它干掉。
下面是一段某 APP 检查其 Webview 加载网站的代码:
xxx.thirdparty.jsonp_callback(
{
"status": "success",
"result": ".banner",
"customize": "if(document.querySelector('div[xxx-ignored-node=\"1\"]')){document.querySelector('div[xxx-ignored-node=\"1\"]').style.display='none';}"
}
)
这种靠黑名单来进行前端拦截的方式可能会有些效果,但弊端也很明显:
- 黑名单主要靠用户反馈维护,麻烦且时效性差。
- 黑名单数量过多可能会影响性能。
- 劫持网站/IP太多,很容易被绕过。
而 CSP 使用白名单机制,目前主流浏览器也支持,所以经过调研后决定采用 CSP 作为解决方案。(最近看到 QQ 邮箱也加上了 CSP,但它的 script-src
里有 unsafe-inline
,不太可能是用来防 XSS 的)。
在加上 CSP 之前,我们先看看运营商劫持的主要手法:
- DNS 劫持。直接篡改 DNS 返回包,比例很小,除了让用户修改 DNS 没什么好办法。
HTTP 劫持。全部或部分替换/注入页面元素,比例很大。
- 遇到返回包全量替换(整个页面或 JS 文件),请用 HTTPS。
- 遇到 script、iframe 等注入,可以用 CSP 防御。
对于 CSP 白名单的获取,打开网页后抓包看看加载了哪些外部资源即可,推荐一个在线工具 Report-URI CSP Generate。
线上小量灰度测试时可以用 Content-Security-Policy-Report-Only
,这将只发送违规资源而不会拦截它。
设置 report-uri
后浏览器会自动向接收地址 POST 违规资源详情,后端只需把数据丢给 ELK
就可以了。
一个典型的运营商劫持案例:
可以看到劫持都发生在同一个省,同一个运营商,劫持次数变化符合作息时间。
对劫持数据聚合分析后发现 湖南电信 和 广东电信 是劫持最多的运营商:
绕过 CSP 的情况
除部分内核较老的浏览器使用 CSP 会有奇怪的 bug(如不支持 script-src),还有一些劫持方法可以绕过 CSP,这里暂不展开……
其他劫持情况
实际运行中发现了一些非运营商的劫持:
- Wi-Fi,某些路由器会在 HTTP 流量中插入广告,银行,酒店,医院等公共场所的 Wi-Fi 劫持也比较普遍。
- 浏览器插件。某些插件会在用户浏览的每个页面都插入广告。
- 恶意软件。发现一些劫持地区很广,各个运营商都存在的域名,和恶意软件有关。
- VPN。某些 VPN 或黑心梯子商会劫持流量插广告。
CSP 的其他用法
开发反运营商劫持系统时有一些其他的脑洞:
- 广告拦截插件。现在的浏览器广告拦截插件都是基于黑名单的,如果做一个基于 CSP 的广告拦截插件应该效果不错,毕竟维护白名单比维护黑名单轻松多了。
- 内部网站盲打监控。外部网站因为业务需要往往不会上 CSP 防御 XSS,不过一些内部网站,比如用户反馈平台,可以接入 CSP 系统,防止 XSS 盲打。