两年前,我写过一篇介绍 Content Security Policy(CSP)的文章(详情),CSP 是一个用来定义页面可以加载或执行哪些资源的协议,目前已经发展到了 Level 2。我在本站之前的文章中已经多次提到过 CSP2,这篇文章也早就躺在我的草稿箱,只是断断续续写了好久才写完。
CSP2 简单介绍
CSP2 基本兼容 CSP1 指令,但有一些变化和新的指令,简单汇总如下:
- 现在配置资源 URL 白名单时,可以使用具体的路径来实现更精细化的规则;
-  现在 Worker 加载策略由新增的      child-src指令控制,而不是script-src;另外,CSP1 中 Worker 的加载策略由父页面决定,而 CSP2 新增了专门针对 Worker 的策略;
-  新增了      base-uri指令,用于指定页面的 base URL;
-  新增了用于指定 frame、Worker 加载策略的      child-src;同时废弃了 CSP1 中用于 frame 的frame-src指令;
-  新增了      form-action指令,用于指定表单提交 URL 白名单;
-  新增了      frame-ancestors指令,用于指定页面被其他页面嵌入时的行为。这部分内容后面详细介绍;
-  新增了      plugin-types指令,用于指定页面允许加载的插件类型;
- inline 代码(JS 和 CSS)现在可以使用 Nonces 和 Hashes 策略来配置白名单。这部分内容后面详细介绍;
-  现在,资源加载过程中违反 CSP 策略时,将会触发      SecurityPolicyViolationEvent事件;
-  现在,触发      SecurityPolicyViolationEvent事件,或者通过report-uri指令指定了上报地址时,增加了更多字段信息便于定位问题;
CSP2 的浏览器支持情况可以通过 CanIUse 查看。目前主流浏览器中,Chrome 和 Firefox 对 CSP2 支持得比较好,IE 和 Safari 比较落后。
 很多 HTTP 响应头都有对应的    <meta> 形式,CSP 也不例外。例如以下二者是等价的:  
Content-Security-Policy: script-src 'nonce-1' 'sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=' <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-1' 'sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng='">  但也有几点需要注意:1)    report-uri 、    frame-ancestors 、    sandbox 这三个指令在通过    <meta> 指定的 CSP 规则中无效;2)CSP    <meta> 标签必须位于    </head> 之前,否则无效;3)迄今为止,Firefox 仍不支持通过    <meta> 标签指定 CSP 策略(    详情 )。所以推荐使用响应头来设置 CSP 规则。  
CSP2 主要变化
Nonces 和 Hashes
 在 CSP1 时代,我们对 inline 资源能采取的策略比较单一,那么全部不允许,要么全部允许。CSP 的目标之一是减少 XSS,如果网站主能够禁用 inline script,对 XSS 确实有致命打击,但这并不现实,太多的网站还是会启用    unsafe-inline 和    unsafe-eval ,让这道防线形同虚设。  
 CSP2 考虑到这种现状,允许给 inline 脚本和样式配置更为精确的规则,这就是    Nonces 和    Hashes 。它们都可以用来确保仅执行已知的 inline 代码,避免 XSS 代码运行。  
Nonces是通过在 CSP 中指定允许资源的序号,达到限制非法 inline 代码的目的。只有与 CSP 策略中序号一致的代码可以执行,具体使用方式如下:
Content-Security-Policy: script-src 'nonce-1' <script nonce="1">alert(0);</script>  实际使用中为了确保安全,    nonce 的值都会经过 base64 编码,并且编码前不少于 128 位,每次请求序号都会改变。总之一个原则:不能让攻击者猜到它的内容。  
Hashes是通过在 CSP 中指定允许资源的摘要签名,达到限制非法 inline 代码的目的。签名规则与 Subresource Integrity 一致,支持 sha256、sha384 和 sha512 三种算法。具体使用方式如下:
Content-Security-Policy: script-src 'sha256-d3ii1Pel57UO62xosCMNgTaZJhJa87Gd/X6e7UdlEU8=' <script>alert(0);</script> Hashes 的值可以通过以下命令获得(以 sha256 为例):
echo -n 'alert(0);' | openssl dgst -sha256 -binary | openssl enc -base64 -A  d3ii1Pel57UO62xosCMNgTaZJhJa87Gd/X6e7UdlEU8= 需要注意的是,inline 代码首尾的空白部分也会计算在 hash 之中。
 可以看出,这两种方式相比直接使用    unsafe-inline ,在安全方面都有很大的提升。其中    Nonces 方案使用成本更低,    Hashes 方案更安全。  
frame-ancestors
     frame-ancestors 指令用来定义页面是否允许被其他页面嵌套,它和X-Frame-Options 非常类似:  
Content-Security-Policy: frame-ancestors 'none' Content-Security-Policy: frame-ancestors 'self' 分别等同于:
X-Frame-Options: DENY X-Frame-Options: SAMEORIGIN  二者不同之处在于:浏览器实现    X-Frame-Options 时,一般只考虑了最顶层域名是否匹配;而    frame-ancestors 要求每一层都需要考虑。另外,CSP2 协议规定二者同时存在时,    X-Frame-Options 应该被忽略,但我在测试过程中发现,Chrome 45+ 并没有按照标准实现。所以有类似需求时,推荐只使用    frame-ancestors ,不要混用。  
CSP2 还有很多其他内容,这里不一一介绍了,有兴趣的同学可以直接查看官方文档,或者留言讨论。
本文链接: https://imququ.com/post/content-security-policy-level-2.html
-- EOF --
于 2015-10-05 00:19:59 发表于「前端技术」分类下。
本站部署于「 阿里云 ECS 」。如果你也要购买阿里云服务,可以使用我的九折推荐码 NY1Z0E ,感谢你对本站的支持!
 
 
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                                     
                ![[HBLOG]公众号](https://www.liuhaihua.cn/img/qrcode_gzh.jpg) 
						 
						