Content Security Policy(CSP)介绍

1,381 阅读4分钟

前言

又到了年末冲kpi的时候,流氓运营商/广告商也蠢蠢欲动。

去年上了HTTPS之后,总算消停了一会,然而今年,它们又卷土重来,变本加厉,连HTTPS都防不住了(代理人攻击)。难道我们就只能坐以待毙,束手就擒么?

CSP(Content Security Policy)

Never!随着浏览器的更新换代,我们有了新的技术方案去确保我们的页面内容是正常可控制的,就是CSP。参考链接:developer.mozilla.org/en-US/docs/…

Content Security Policy 是一个额外的安全层,用于检测和削弱某种类型的攻击,例如XSS和数据注入攻击。

使用方法

可以通过以下两种方式声明CSP规则

  1. 服务器返回头声明,例如在nginx配置
add_header Content-Security-Policy "default-src 'self'";
  1. 在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-toCSP: 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'
  • 所有资源获取都只能在当前源,其他的源都会被屏蔽并报错,例如

www.other-website.com

web.my-website.com

www.my-website.com

www.my-website.com:9897

  • 行内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解析指向某个有害的文件这种骚操作),还需要其他措施配合。这是一场永不停息的战斗。