网页调用第三方文件,integrity属性如何检验文件没被篡改?
发布于 作者:苏南大叔 来源:程序如此灵动~很多程序员网页编程的时候,经常会调用其它域名(例如各大cdn
)提供的js
或者css
文件。这些文件都是一些公共的指明第三方库文件,比如jquery
/bootstrap
/google font
/tongji.baidu.com
等等。据说使用这样的cdn
服务,会加快网页浏览速度...实际上苏南大叔并不赞同这种做法,使用这种第三方的cdn
服务,必须要承担的风险有:cdn
被墙,或者第三方文件被篡改。你敢保证:你调用的第三方文件是你要调用的版本么?
目前,浏览器支持使用integrity
属性来做个哈希判断,如果hash
值不符,浏览器则拒绝执行对应的文件。(但是,文件肯定是被下载到本地了,只是没有被浏览器执行罢了)。本文测试环境:win10
,nginx@1.15.11
,openssl@3.2.0
。
integrity
,翻译为:“诚实正直”。
写在前面
首先说明的是:苏南大叔并不是很相信这种第三方提供的各种免费cdn
服务。所以,苏南大叔的做法是:在临时测试的时候,会使用这种服务。当进入到稍稍正式的环境的时候,就会把这些第三方服务的文件,放到自己的服务器上面去。【肯定会被大家不同意的,但是个人觉得这样做才很放心】。
本文的integrity
属性的hash
值,是通过openssl
工具计算出来的。所以,如果对方没有提供标准的计算值的话。你可能需要使用openssl
工具自己计算一个。如何安装openssl
,可以参考文章:
- https://newsn.net/say/openssl-win.html
- https://newsn.net/say/mac-brew-openssl.html
- https://newsn.net/say/mac-openssl-source.html
可能还需要使用到curl
命令,参考:
准备被调用文件
本文使用nginx
模拟了两个网站:
- 正常意义上的网站
test
。 - 被调用的第三方网站
cdn
,cdn
上放了几个文件:css.css
、js.js
文件。
先准备好被检查的文件到本地,用于hash
检测。实际的情况下,各位命令行极客,可以使用curl
工具,这里的选择很多。
算法sha256
、sha512
、sha384
使用openssl
工具,在命令行下面进行计算这个integrity
值。sha256
、sha512
、sha384
,任选一个就行,效果都一样。注意替换sha256
字样为目标算法,下面的命令中使用sha256
做例子。
推荐这样使用:
openssl dgst -sha256 -binary js.js | openssl base64 -A
也可以搭配curl
命令使用:
curl http://cdn/js.js | openssl dgst -sha256 -binary | openssl enc -base64 -A
得到文件的integrity
值,就是:
<算法>-<算法得到的hash值>
例如:
sha256-kiZqYorpFWj0fPn0Oum3YzZ6ur1+PIltESUCspqMUFM=
使用方式
使用方式如下,注意存在两个属性:integrity='shaxxx-xxxxxxxx'
和crossorigin='anonymous'
。
<link href="//cdn/css.css" integrity="sha256-kOinKz8NQC7h1yA/Awp8wQ5mraIkcmil7+pdmdKOmsk=" crossorigin="anonymous" rel="stylesheet"/>
<script src="//cdn/js.js" integrity="sha256-kiZqYorpFWj0fPn0Oum3YzZ6ur1+PIltESUCspqMUFM=" crossorigin="anonymous"></script>
值得说明的是:正常情况下来说,不通过integrity
属性,验证其hash
值的情况下,简单的调用下,可以直接执行的。没有任何的阻力,反而是integrity
和crossorigin
使得事情变得复杂了。
<link href="//cdn/css.css" rel="stylesheet"/>
<script src="//cdn/js.js"></script>
跨域
然而本文的integrity
属性这样使用的话,会出现新的问题,那就是【跨域】。也算是一种跨域调用。对于新增加的integrity
属性和crossorigin
属性,本来很普通的文件调用。也变成了复杂多变的跨域行为。【真心是给自己出难题啊】
下面以nginx
设置最简单的跨域方案来解决问题【实际情况会很复杂】。如下设置:
location / {
add_header 'Access-Control-Allow-Origin' $http_origin always;
}
当然,同时记得调用的时候,设置【crossorigin="anonymous"】。否则会提示,错误信息:
Access to CSS stylesheet at 'http://cdn/css.css' from origin 'http://test' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
或者:
Subresource Integrity: The resource 'http://cdn/js.js' has an integrity attribute, but the resource requires the request to be CORS enabled to check the integrity, and it is not. The resource has been blocked because the integrity cannot be enforced.
检测失败
如果integrity
值检测失败,则页面会拒绝执行对应脚本【大概率就是传说中的cdn
污染】。报错如下:
Failed to find a valid digest in the 'integrity' attribute for resource 'http://cdn/css.css' with computed SHA-256 integrity 'kOinKz8NQC7h1yA/Awp8wQ5mraIkcmil7+pdmdKOmsk='. The resource has been blocked.
反转一,图片和字体
从第三方cdn
加载图片和字体,似乎和integrity
属性没有关系。
img
乱写了integrity
属性。font
加载,没地方写integrity
属性。
反转二,替换被引用文件
表面上来看,这种浏览器支持的integrity
属性,是非常好用的。然而,仔细想想看的话。这个integrity
也不是无懈可击。比如:
- 它调用计算的是表面上这个文件的
hash
值,那么,如果是文件调用文件的形式呢?被调用的代码确实没变,变的是下一个被隐式调用的文件。那么,最终的效果就是成功绕过了这个integrity
限制。 - 现在流行的做法是:
html
文件都放在cdn
上面。所以,问题来了。如果存放自己的静态文件的cdn
,也被污染了呢?integrity
值被替换成了一个邪恶版本的值。
相关文章
这里有个在线计算hash
值的网站:
总结
苏南大叔在这篇文章的观点,是与大多数人的观点相左的。所以,大家请轻喷。本文的实际调用代码里面,还使用了crossorigin="anonymous"
这个属性,这个留在后续文章讨论。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。