前言
又到了年末冲kpi的时候,流氓运营商/广告商也蠢蠢欲动。
去年上了HTTPS之后,总算消停了一会,然而今年,它们又卷土重来,变本加厉,连HTTPS都防不住了(代理人攻击)。难道我们就只能坐以待毙,束手就擒么?
CSP(Content Security Policy)
Never!随着浏览器的更新换代,我们有了新的技术方案去确保我们的页面内容是正常可控制的,就是CSP。参考链接:developer.mozilla.org/en-US/docs/…
Content Security Policy 是一个额外的安全层,用于检测和削弱某种类型的攻击,例如XSS和数据注入攻击。
使用方法
可以通过以下两种方式声明CSP规则
- 服务器返回头声明,例如在nginx配置
add_header Content-Security-Policy "default-src 'self'";
- 在html文件的head里面使用meta标签声明
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
需要注意的是,如果两种方式都声明了相同的指令(如script-src),以服务器返回头为准;若服务器返回头声明的是default-src,将会覆盖所有的meta标签声明
策略声明
{指令} {来源} {来源};
常用的指令如下:
获取指令
- default-src (备用指令,用于指定没有被特别声明的资源)
- script-src (script标签指令)
- frame-src (iframe/frame标签指令)
- img-src (图片指令,包括img标签、video标签的poster属性、样式的background-image、JS创建Image对象等)
- style-src (样式指令)
- media-src (媒体audio/video/track标签指令)
- object-src (对象标签指令,包括object、embed、applet标签)
获取指令的常用的合法来源可以是以下几个:
- 域名来源 (如www.example.com、https://.example.com、.example.com:443、192.168.0.1)
- 协议 (如http:、https:、data:、blob:等)
- 'self' (当前的源)
- 'unsafe-eval' (允许eval、Function()、setTimeout、setInterval、setImmediate这些通过字符串创建代码的方法调用)
- 'unsafe-inline' (允许行内script)
- 'nonce-{base64-value}' (不合法script标签,如不符合域名来源规则和行内标签,只有带有相同的nonce的值才会被执行;当此来源条件生效时,unsafe-inline无效)
- '{hash-algorithm}-{base64-value}' (为行内script指定合法的hash算法校验以及校验结果,只有校验通过才能执行;当此来源条件生效时,unsafe-inline无效)
- 'none' (不匹配任何来源,相当于不允许任何来源执行;只有当它是唯一来源时生效,多个来源之一时报错)
报告指令
- report-uri / report-to (上报指令,report-uri将会逐渐废弃,支持report-to的浏览器将自动忽略report-uri)
这块没怎么使用过,详情请查看 CSP: report-to、CSP: report-uri 以及 Content-Security-Policy-Report-Only
其他指令
- block-all-mixed-content (当页面是https加载时,阻止混合资源加载)
- upgrade-insecure-requests (http协议请求自动升级为https)
更多更详细的内容请到 这里
举例时间
以下例子假定当前页面地址为 https://www.my-website.com
Example 1:
Content-Security-Policy: default-src 'self'
- 所有资源获取都只能在当前源,其他的源都会被屏蔽并报错,例如
- 行内JS和样式都不允许,例如
<!-- 行内代码不执行 -->
<style>
...
</style>
<!-- 行内代码不执行 -->
<script>
...
</script>
<!-- 行内样式不执行 -->
<button style="color: #000">Click Me</button>
Example 2:
Content-Security-Policy: script-src 'self' *.my-website.com 'unsafe-eval' 'nonce-2bd73a4533'; img-src 'self';
JS资源只允许当前源、my-website.com的二级域名,且允许类eval方法执行。
以下代码中,
引入不合法域名JS以及行内JS代码不执行(行内代码包括<script>标签以及事件处理如onclick等);
nonce值不正确的行内代码不执行;
图片引用不合法的域名,不加载。
<style>
.submit-btn {
/* 域名不合法,图片不加载 */
background-image: url(//static.my-website.com/public/images/logo.png);
}
</style>
<!-- 域名合法 -->
<script type="text/javascript" src="//www.my-website.com/assets/js/main.js"></script>
<script type="text/javascript" src="//static.my-website.com/public/js/jquery.js"></script>
<!-- 域名不合法 -->
<script type="text/javascript" src="//www.other-website.com/assets/js/index.js"></script>
<!-- 行内代码不执行 -->
<script>
console.log('lol');
</script>
<!-- 行内代码nonce不正确 -->
<script nonce="a123b345cd">
console.log('T_T');
</script>
...
<!-- 行内代码不执行 -->
<button class="submit-btn" onclick="clickEvtHandler();">Click Me</button>
<!-- 行内代码nonce正确,正常执行 -->
<script nonce="2bd73a4533">
var imgElm = new Image();
// 域名不合法,图片不加载
imgElm.src = '//www.other-website.com/assets/images/icn.png';
console.log('Done');
</script>
console输出
Done
Example 3:
Content-Security-Policy: style-src 'self' 'unsafe-inline'
允许当前源以及行内样式
Example 4:
Content-Security-Policy: frame-src *.my-website.com *.trust-website.com
允许my-website.com和trust-website.com及其二级域名的页面嵌套在iframe里面
Example 5:
Content-Security-Policy: media-src http: blob:; object-src 'none'; img-src 'self' data:;
- 媒体标签允许http协议、blob协议
- 不允许任何对象标签的使用
- 图片允许当前源或者data协议
Example 6:
Content-Security-Policy: upgrade-insecure-requests
所有http请求都升级为https
<script type="text/javascript" src="http://www.my-website.com/assets/js/main.js"></script>
...
<img src="http://static.my-website.com/public/images/logo.png">
喜闻乐见的兼容性环节
IE还是一如既往的。。。算了,我们忽略它吧。针对iOS Safari需要声明X-Webkit-CSP,其他主流浏览器都完全兼容。
最后
CSP的出现,使得Web开发者对抗XSS和代码注入有了更优雅、更直观的处理方案(在现代浏览器内)。但是,世上从没有银弹,还是有很多问题(例如运营商在页面上注入一个同域名但本身不存在的文件,然后DNS解析指向某个有害的文件这种骚操作),还需要其他措施配合。这是一场永不停息的战斗。