SRI (Subresource Integrity) 是一個透過瀏覽器驗證引用的第三方資源,確保內容沒有被串改。
例如,我們常會引用第三方的 CDN 來源,以加快請求的時間。但由於 CDN 來源分散,難以進行定位,若其中一個來源節點受到劫持,可能會發生隨機的劫持,並且難以重複呈現,用戶刷新頁面後,就無法再重現。
因此,可透過 SRI 的方式,是在 script 或 link 增加 integrity 屬性,當瀏覽器讀取到 integrity 屬性,就會進行 SRI 驗證。
integrity 主要分成兩部分,hash演算法 以及 經過 bash64編碼的hash值,兩者透過 “-” 來連結,例如:
另外,透過 crossorigin 來聲明可以跨域
<script type="text/javascript" src="//cdnurl.xxx.js"
integrity="sha256-xxxx sha384-xxxxx"
onerror="loadScriptError.call(this, event)"
crossorigin="anonymous"></script>
當發生錯誤時,重新載入本地資源
(function () {
function loadScriptError (event) {
// 上报
...
// 重新加载 js
return new Promise(function (resolve, reject) {
var script = document.createElement('script')
script.src = this.src.replace(/\/\/11.src.cn/, 'https://x.y.z') // 替换 cdn 地址为静态文件服务器地址
script.onload = resolve
script.onerror = reject
script.crossOrigin = 'anonymous'
document.getElementsByTagName('head')[0].appendChild(script)
})
}
function loadScriptSuccess () {
// 上报
...
}
window.loadScriptError = loadScriptError
window.loadScriptSuccess = loadScriptSuccess
})()
使用 webpack
使用 Webpack 可以透過以下方式來生成 SRI
import SriPlugin from 'webpack-subresource-integrity'
const compiler = webpack({
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SriPlugin({
hashFuncNames: ['sha256', 'sha384'],
enabled: process.env.NODE_ENV === 'production',
})
]
})
webpack 注入 onerror
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin(),
new SriPlugin({
hashFuncNames: ['sha256', 'sha384']
}),
new ScriptExtHtmlWebpackPlugin({
custom: {
test: /\/*_[A-Za-z0-9]{8}.js/,
attribute: 'onerror',
value: 'loadScriptError.call(this, event)'
}
}),
new ScriptExtHtmlWebpackPlugin({
custom: {
test: /\/*_[A-Za-z0-9]{8}.js/,
attribute: 'onsuccess',
value: 'loadScriptSuccess.call(this, event)'
}
})
]
}