网页调用第三方文件,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
做例子。
推荐这样使用:
也可以搭配curl
命令使用:
得到文件的integrity
值,就是:
例如:

使用方式
使用方式如下,注意存在两个属性:integrity='shaxxx-xxxxxxxx'
和crossorigin='anonymous'
。

值得说明的是:正常情况下来说,不通过integrity
属性,验证其hash
值的情况下,简单的调用下,可以直接执行的。没有任何的阻力,反而是integrity
和crossorigin
使得事情变得复杂了。
跨域
然而本文的integrity
属性这样使用的话,会出现新的问题,那就是【跨域】。也算是一种跨域调用。对于新增加的integrity
属性和crossorigin
属性,本来很普通的文件调用。也变成了复杂多变的跨域行为。【真心是给自己出难题啊】
下面以nginx
设置最简单的跨域方案来解决问题【实际情况会很复杂】。如下设置:
当然,同时记得调用的时候,设置【crossorigin="anonymous"】。否则会提示,错误信息:
或者:
检测失败
如果integrity
值检测失败,则页面会拒绝执行对应脚本【大概率就是传说中的cdn
污染】。报错如下:
反转一,图片和字体
从第三方cdn
加载图片和字体,似乎和integrity
属性没有关系。
img
乱写了integrity
属性。font
加载,没地方写integrity
属性。

反转二,替换被引用文件
表面上来看,这种浏览器支持的integrity
属性,是非常好用的。然而,仔细想想看的话。这个integrity
也不是无懈可击。比如:
- 它调用计算的是表面上这个文件的
hash
值,那么,如果是文件调用文件的形式呢?被调用的代码确实没变,变的是下一个被隐式调用的文件。那么,最终的效果就是成功绕过了这个integrity
限制。 - 现在流行的做法是:
html
文件都放在cdn
上面。所以,问题来了。如果存放自己的静态文件的cdn
,也被污染了呢?integrity
值被替换成了一个邪恶版本的值。

相关文章
这里有个在线计算hash
值的网站:
总结
苏南大叔在这篇文章的观点,是与大多数人的观点相左的。所以,大家请轻喷。本文的实际调用代码里面,还使用了crossorigin="anonymous"
这个属性,这个留在后续文章讨论。


