我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

很多程序员网页编程的时候,经常会调用其它域名(例如各大cdn)提供的js或者css文件。这些文件都是一些公共的指明第三方库文件,比如jquery/bootstrap/google font/tongji.baidu.com等等。据说使用这样的cdn服务,会加快网页浏览速度...实际上苏南大叔并不赞同这种做法,使用这种第三方的cdn服务,必须要承担的风险有:cdn被墙,或者第三方文件被篡改。你敢保证:你调用的第三方文件是你要调用的版本么?

苏南大叔:网页调用第三方文件,integrity属性如何检验文件没被篡改? - integrity属性
网页调用第三方文件,integrity属性如何检验文件没被篡改?(图5-1)

目前,浏览器支持使用integrity属性来做个哈希判断,如果hash值不符,浏览器则拒绝执行对应的文件。(但是,文件肯定是被下载到本地了,只是没有被浏览器执行罢了)。本文测试环境:win10nginx@1.15.11openssl@3.2.0

integrity,翻译为:“诚实正直”。

写在前面

首先说明的是:苏南大叔并不是很相信这种第三方提供的各种免费cdn服务。所以,苏南大叔的做法是:在临时测试的时候,会使用这种服务。当进入到稍稍正式的环境的时候,就会把这些第三方服务的文件,放到自己的服务器上面去。【肯定会被大家不同意的,但是个人觉得这样做才很放心】。

本文的integrity属性的hash值,是通过openssl工具计算出来的。所以,如果对方没有提供标准的计算值的话。你可能需要使用openssl工具自己计算一个。如何安装openssl,可以参考文章:

可能还需要使用到curl命令,参考:

准备被调用文件

本文使用nginx模拟了两个网站:

  • 正常意义上的网站test
  • 被调用的第三方网站cdncdn上放了几个文件:css.cssjs.js文件。

先准备好被检查的文件到本地,用于hash检测。实际的情况下,各位命令行极客,可以使用curl工具,这里的选择很多。

算法sha256sha512sha384

使用openssl工具,在命令行下面进行计算这个integrity值。sha256sha512sha384,任选一个就行,效果都一样。注意替换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属性如何检验文件没被篡改? - sha256-hash
网页调用第三方文件,integrity属性如何检验文件没被篡改?(图5-2)

使用方式

使用方式如下,注意存在两个属性: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属性如何检验文件没被篡改? - 运行截图
网页调用第三方文件,integrity属性如何检验文件没被篡改?(图5-3)

值得说明的是:正常情况下来说,不通过integrity属性,验证其hash值的情况下,简单的调用下,可以直接执行的。没有任何的阻力,反而是integritycrossorigin使得事情变得复杂了。

<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属性如何检验文件没被篡改?(图5-4)

反转二,替换被引用文件

表面上来看,这种浏览器支持的integrity属性,是非常好用的。然而,仔细想想看的话。这个integrity也不是无懈可击。比如:

  • 它调用计算的是表面上这个文件的hash值,那么,如果是文件调用文件的形式呢?被调用的代码确实没变,变的是下一个被隐式调用的文件。那么,最终的效果就是成功绕过了这个integrity限制。
  • 现在流行的做法是:html文件都放在cdn上面。所以,问题来了。如果存放自己的静态文件的cdn,也被污染了呢?integrity值被替换成了一个邪恶版本的值。

苏南大叔:网页调用第三方文件,integrity属性如何检验文件没被篡改? - 只替换部分文件
网页调用第三方文件,integrity属性如何检验文件没被篡改?(图5-5)

相关文章

这里有个在线计算hash值的网站:

总结

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

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   cors