网页安全之CSP和SRI

date
Nov 5, 2022
slug
/security
status
Published
tags
SRI
CSP
summary
网页安全之CSP和SRI
type
Post
最近在看网页安全相关内容,碰巧看到与一篇文章《第三方依赖包里居然有投毒代码》,讲的是引用第三放 CDN内容存在恶意代码https://cdn.bootcss.com/vConsole/3.3.0/vconsole.min.js,在访问这个 cdn内容时候有几率看到下图的恶意代码
notion image
这种问题一出现就很难排查,毕竟想不到这种出名的库也会有恶意代码,当然这个问题的罪魁祸首不是库的问题,而是第三方 CDN问题。
解决方案也只能杜绝使用第三方 CDN内容,只使用库本身代码基本不会出现问题。我在想有什么方案可以根本上解决这个问题呢?引申出来怎么确保第三方库的安全问题呢?
 

内容安全策略Content Security Policy(CSP)

CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。
CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
两种方法可以启用 CSP。一种是通过 HTTP 头信息的Content-Security-Policy的字段。
notion image
Content-Security-Policy: script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:
另一种是通过网页的<meta>标签。
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
上面代码中,CSP 做了如下配置
  • 脚本:只信任当前域名
  • <object>标签:不信任任何URL,即不加载任何资源
  • 样式表:只信任cdn.example.orgthird-party.org
  • 框架(frame):必须使用HTTPS协议加载
  • 其他资源:没有限制
 
也可以直接像下面设置
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
这个表示网页上的所有非 https请求都转成 https请求,包括图片、域名链接、异步接口等,这样可以杜绝在 https环境下发送http请求(当然现在浏览器已限制此类请求)
 
需要注意的是,如果web server配置了csp,html文件中也用meta配置了csp,但是配置的value是不同的。那么最终生效的CSP安全策略是权限范围最小的那个,与CSP来自HTTP response header还是meta无关。
 
CSP 提供了很多限制选项,涉及安全的各个方面,具体可查看 MDN
 

子资源完整性Subresource Integrity(SRI)

这个用的相对少一些,子资源完整性(SRI) 是允许浏览器检查其获得的资源(例如从 CDN 获得的)是否被篡改的一项安全特性。它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改。
 
很多时候我们使用CDN多个站点之间共享了脚本和样式,以便提高网站性能节省宽带。然而也存在风险,如果攻击者获取了CDN的控制权,就可以将任意内容恶意注入到CDN文件中,从而攻击了加载此CDN资源的站点。所以就需要 SRI 来确保Web应用程序获得的文件未经过第三方注入或者其他形式的修改来降低被攻击的风险。

SRI 原理

将文件内容通过 base64 编码 后的哈希值,写入你所引用的 <script><link> 标签的 integrity 属性值中即可启用子资源完整性功能。浏览器在加载此内容执行之前,会判断该文件的哈希值是否和 integrity 预期的一致,只有一致才会执行。
 
<script src="https://example.com/example-framework.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script>
 
注意script标签的 integrity必须和crossorigin=anonymous一起使用。
 
当浏览器下载了带有integrity属性的子资源的时候,不会立刻执行里面的代码;或者应用里面的样式。浏览器会首先根据integrity属性值中指定的相应算法以及下载的文件的内容计算一下这个文件的哈希值是否跟标签中的那个值一样,只有两者一样的情况下才会应用对应的样式或者执行相应的代码。如果两者不一样,那么浏览器就会拒绝执行对应的代码,以及拒绝应用对应的样式。也会在控制台报错,提醒我们当前下载的子资源存在问题。
 
可以根据内容安全策略(CSP)来配置你的服务器使得指定类型的文件遵守 SRI。这是通过在 CSP 头部 添加 require-sri-for 指令实现的:
Content-Security-Policy: require-sri-for script;
这条指令规定了所有 JavaScript 都要有 integrity 属性,且通过验证才能被加载。
所以,只要文件变化了,浏览器就不会执行,有效避免了脚本攻击。

© yujun 2021 - 2025