В последнее время проект часто сталкивался с захватом CDN, и я узнал, что с этим можно эффективно бороться с помощью Subresource Integrity.
Введение в НИИ
SRIПолное название Subresource Integrity — Subresource Integrity относится к функции безопасности, которую браузеры используют для проверки целостности ресурсов (обычно получаемых из CDN), чтобы определить, не были ли они подделаны.
Улучшите функцию SRI, добавив свойство Integrity в тег LINK или тег Script, например:
<script type="text/javascript" src="//s.url.cn/xxxx/aaa.js"
integrity="sha256-xxx sha384-yyy"
crossorigin="anonymous"></script>
Значение целостности разделено на две части: первая часть определяет алгоритм генерации хеш-значения (sha256, sha384 и sha512), а вторая часть представляет собой фактическое хэш-значение, закодированное с помощью base64, разделенное дефисом (-). Значение целостности может содержать несколько хэшей, разделенных пробелами, и если файл соответствует любому из этих хэшей, ресурс можно проверить и загрузить. В приведенном выше примере я использовал две схемы хеширования sha256 и sha384.
Примечание:
crossorigin="anonymous"
Функция состоит в том, чтобы ввести междоменные сценарии. В HTML5 есть способ получить информацию об ошибках междоменных сценариев. Во-первых, сервер междоменных сценариев должен разрешить текущему доменному имени получать информацию об ошибках через доступ -Controll-Allow-Origin информация заголовка, а затем Тег скрипта, который является текущим доменным именем, должен также объявить, что он поддерживает междоменный атрибут, то есть атрибут crossorigin. Такие теги, как link и img, поддерживают междоменные сценарии. Если вышеуказанные два условия не могут быть выполнены, вы можете использоватьtry catch
строить планы.
Зачем использовать СИ
В веб-разработке использование ресурсов CDN может эффективно сократить время сетевых запросов, но также существует проблема с использованием ресурсов CDN, поскольку ресурсы CDN существуют на сторонних серверах и не полностью контролируются с точки зрения безопасности.
Перехват CDN — очень сложная проблема для обнаружения.Во-первых, угонщик будет использовать определенный алгоритм или случайный метод, чтобы захватить его (хитро), поэтому его очень трудно воспроизвести, и многие пользователи обновят страницу после появления и больше не появится. Ранее коллега в компании сталкивался с этой проблемой с загрузчиком игры.После того как пользователь скачал игру, после распаковки в нее нельзя было играть.Позже путем сравнения файлов по одному была найдена причина.Выяснилось быть вызвано перехватом CDN. Как это решить? Я слышал, что плата за защиту была оплачена на xx, а метод использования хэша файла был использован позже.Он должен быть таким же, как SRI в принципе.
К счастью, большая часть текущего угона CDN заключается в том, чтобы просто выполнить какое-то увлечение, например, вставить некоторые рекламные исправления через iframe, если у угонщика есть скрытые мотивы, такие как внедрение xss, это все еще очень опасно.
Включение SRI может эффективно обеспечить целостность ресурсов ссылок на страницы и избежать выполнения вредоносного кода.
Как браузеры обрабатывают SRI
- Когда браузер встречает атрибут целостности в скрипте или теге ссылки, он сравнивает хэш загруженного файла с ожидаемым хешем перед выполнением скрипта или применением таблицы стилей.
- Когда хэш сценария или таблицы стилей не соответствует ожидаемому, браузер ДОЛЖЕН отказаться от выполнения сценария или применения таблицы стилей и ДОЛЖЕН вернуть сетевую ошибку, указывающую, что получение сценария или таблицы стилей не удалось.
Использование СИ
С помощью модулей webpack html-webpack-plugin и webpack-subresource-integrity можно генерировать теги сценариев, содержащие атрибут целостности.
import SriPlugin from 'webpack-subresource-integrity'
const compiler = webpack({
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SriPlugin({
hashFuncNames: ['sha256', 'sha384'],
enabled: process.env.NODE_ENV === 'production',
})
]
})
Итак, что мне делать, если проверка SRI скрипта или ресурса ссылки не удалась?
Лучший способ — использовать событие скрипта onerror для перезагрузки ресурсов между статическими файловыми серверами при обнаружении onerror:
<script type="text/javascript" src="//11.url.cn/aaa.js"
integrity="sha256-xxx sha384-yyy"
crossorigin="anonymous"
onerror="loadScriptError.call(this, event)"
onsuccess="loadScriptSuccess"></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
})()
Что более болезненно, так это то, что событие в onerror не может определить, что вызвало ошибку.Может быть, что ресурс не существует, или может быть, что проверка SRI не удалась.Конечно, наиболее частым случаем является тайм-аут запроса, но в настоящее время, если нет статистического требования, нет большой проблемы с неизбирательным обращением.
Внедрить событие onerror
Конечно, так как теги script в проекте упакованы webpack, нам нужно использоватьscript-ext-html-webpack-pluginДобавьте события onerror и onsuccess:
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)'
}
})
]
}
Затем внедрите методы loadScriptError и loadScriptSuccess в html, вы можете использовать встроенный метод.
Как судить о захвате CDN?
Как упоминалось ранее, сбой загрузки скрипта может быть вызван различными причинами, так как же определить, произошел ли перехват CDN?
Метод заключается в том, чтобы запросить данные еще раз, дважды сравнить содержимое файла (конечно, сравнивать все не обязательно), если содержимое несовместимо, можно сделать вывод.
function loadScript (url) {
return fetch(url).then(res => {
if (res.ok) {
return res
}
return Promise.reject(new Error())
}).then(res => {
return res.text()
}).catch(e => {
return ''
})
}
Сравните, является ли скрипт, загруженный дважды, одинаковым
function checkScriptDiff (src, srcNew) {
return Promise.all([loadScript(src), loadScript(srcNew)]).then(data => {
var res1 = data[0].slice(0, 1000)
var res2 = data[1].slice(0, 1000)
if (!!res1 && !!res2 && res1 !== res2) {
// CDN劫持事件发生
}
}).catch(e => {
// ...
})
}
Почему здесь сравниваются только первые 1000 символов? Поскольку угонщики CDN обычно внедряют некоторый код в верхнюю часть js-файла для достижения своей цели, внедрение промежуточного кода требует синтаксического анализа AST, который является дорогостоящим, поэтому бессмысленно сравнивать все строки. Если у вас все еще есть проблемы, вы можете добавить сравнение последних n символов.
наконец
Я также видел великого бога на Zhihu, который нашел другой способ решить проблему захвата CDN, подобный jsonp. Лично я считаю, что этот метод отлично справляется с перехватом CDN. Основная причина в том, что операторы осуществляют перехват через сопоставление имен файлов. Метод автора заключается в обнаружении перехвата через onerror и удалении суффикса js файлов ресурсов для борьбы с перехватом CDN. .
Что может сделать внешний интерфейс, чтобы справиться с перехватом трафика?
Эта статья имеет четкую идею и настоятельно рекомендуется для изучения.
Технологический еженедельник IVWEBШок в сети, обратите внимание на публичный номер: сообщество IVWEB, регулярно каждую неделю публикуйте качественные статьи.
- Сборник статей еженедельника:weekly