И чтобы всё было безопасно.

Чуть-чуть про Subresource Integrity (SRI)
Subresource Integrity (SRI) — это функция безопасности, которая позволяет браузерам проверять, не был ли ресурс (скрипт или стиль), загруженный с CDN, изменён.
SRI работает путем добавления атрибута integrity
к вашим ресурсами, например <script>
или <link>
, который содержит криптографический хэш ожидаемого файла.
Выглядит это так:
<script
src="https://cdn.jsdelivr.net/bla/bla/bootstrap.bundle.min.js"
integrity="sha256-tG5mcZUtJsZvyKAxYLVXrmjKBVLd6VpVccqz/r4ypFE="
crossorigin="anonymous"
></script>
Но подождите, а зачем нам нужен SRI?
Если вы используете CDN, то он точно нужен.
Ремарка про CDN
Наверняка вы в курсе, что такое CDN, особенно, если уже используете или планируете использовать. Но я и не буду растекаться про CDN. Нам важны пара моментов.
CDN — это географически распределенная сеть серверов, предназначенная для быстрой доставки веб-контента пользователям. А это значит, что CDN хранит копии вашего сайта (изображения, видео, скрипты, CSS-файлы) на серверах по всему миру. И когда вы, будучи удалёнщиком из Вьетнама, скачиваете мем из рабочего чата от коллеги в Москве, то данные, вместо того чтобы проходить много-много километров, скачиваются с ближайшего сервера, где-нибудь в ЮВА. При этом CDN оптимизирует и сжимает контент, а также использует протоколы, ускоряющие передачу и удалёнщик доволен.
Но...
CDN — это сторонний сервис. И он может (и будет) подвержен атакам, самая простая из которых — подмена содержимого файлов. А это может привести сами знаете к чему. Один из наиболее известных примеров — это инцидент с подменой файла jquery.js на CDN CDNJS, который произошёл в 2021 году. Злоумышленник получил доступ к репозиторию на GitHub, связанному с CDNJS, и заменил файл jquery.js на его вредоносную версию.
Что произошло?
Злоумышленник получил доступ к учетной записи разработчика на GitHub, которая имела права на обновление файлов в репозитории, используемом для CDNJS.
Вместо оригинального кода jQuery злоумышленник загрузил версию, которая содержала дополнительный вредоносный код. Этот код был разработан для кражи данных из форм на сайтах, использующих эту библиотеку.
Поскольку CDNJS обслуживает тысячи веб-сайтов, вредоносный код мог быть доставлен миллионам пользователей. Сайты, которые использовали этот конкретный файл с CDNJS, автоматически загружали его вредоносную версию, даже не подозревая об этом.
А ещё есть:
Кэш-отравление (Cache Poisoning). Злоумышленники внедряют вредоносный скрипт в кэшируемый контент, который затем распространяется на всех пользователей, запрашивающих этот ресурс.
Так, например, в 2020 году исследователи безопасности из компании Akamai обнаружили новый тип атаки, который они назвали «HTTP/2 Cache Deception». Эта атака использовала специфическую особенность протокола HTTP/2, чтобы обманом заставить сервер кэшировать вредоносный ответ.Компрометация TLS-сертификатов: В случае взлома сертификата CDN, злоумышленники могут выдавать себя за CDN и перехватывать зашифрованный трафик.
Один из самых известных и ярких примеров компрометации TLS-сертификатов, который оказал прямое влияние на CDN и всю интернет-безопасность, — это уязвимость Heartbleed в библиотеке OpenSSL в 2014 году.Утечка IP-адреса исходного сервера: Некорректные настройки CDN могут раскрыть IP-адрес основного сервера, делая его уязвимым для прямых атак, минуя защиту CDN.
Как мы видим из примеров компрометации CDN, злоумышленник получает мощнейший инструмент распространения вредоносного кода.
Как обезопасить сайт от подмены? SRI.
Если говорить простыми словами, то это функция безопасности, которая позволяет браузерам проверять, что файлы, загружаемые с внешних источников, не были изменены или подделаны.
Я встретился с необходимостью внедрить SRI, когда для Альфа-Онлайн нам необходимо было внедрить CDN, но так как это авторизованная зона и скрипты непосредственно работают с данными пользователей, было критично проверять скрипты.
Принцип работы SRI
SRI работает так:
На сервере или клиенте к файлу добавляется атрибут
integrity
к тегу<script>
или<link>.
Когда браузер загружает файл/скрипт, то перед выполнением браузер вычисляет его хэш и сравнивает с хэшем, указанным в атрибуте
integrity
.Если хэши не совпадают, то браузер блокирует выполнение скрипта, защищая страницу от атаки. Это помогает предотвратить атаки, при которых код был изменен на сервере CDN или в пути к пользователю.
<script
type="text/javascript"
crossorigin="anonymous"
src="https://alfaonline.servicecdn.ru/public/newclick-host-ui/assets/desktop.d4540de8.js"
integrity="sha256-Xk9O4fZZD2REXgSnFuE9HvXfKoXAmdj6mdwMOuOrbwk=">
</script>
Как правильно интегрировать SRI под ваш проект и сборщик, чтобы он автоматически добавлял SRI для всех ваших ассетов, в том числе динамических? То есть скрипты, которые грузятся от других скриптов, которые выглядят под капотом примерно так:

Варианты:
С поддержкой все хорошо, даже в Safari она уже давно есть

Сложность внедрения SRI в микрофронтах
Внедрение SRI в микрофронтах имеет свои особенности и может быть сложнее, чем в монолитных приложениях.
Динамическая загрузка: микрофронты часто загружают ресурсы динамически, что усложняет статическую проверку хешей. Хеши могут меняться с каждой новой версией компонента, что требует автоматизации процесса их обновления.
Синхронизация: каждый микрофронт может иметь свои зависимости и версии, что создаёт проблемы с синхронизацией хешей. Если один из компонентов обновляется, остальные должны быть в курсе изменений, чтобы использовать правильные SRI-значения.
Сложность сборки: внедрение SRI требует включения шагов генерации хешей в процесс сборки каждого микрофронта. Это может усложнить CI/CD пайплайны.
В Module Federation есть специальный файл remoteEnty.js, который загружает остальные скрипты сайта, учитывая все переиспользуемые зависимости и многую логику, которая есть в MF, но из за того, что мы не знаем, что находится в этой файле, необходимо добавлять дополнительную логику в деплое микрофронтов.
На примере показана загрузка файла remoteEntry и всех его зависящих скриптов.

Как мы решили эти проблемы?
Добавили в пайплайн любой части приложения хук, который вызывается в момент деплоя приложения, а хук в свою очередь берёт заранее подготовленный файл и запихивает его в базу. Сейчас более подробно как работает (по шагам).
№ 1. На момент конца сборки мы создаём специальный файл через rsbuild плагин critical-assets.json
. Выглядит он так:
{
"remoteEntry": {
"src": "/assets/remoteEntry.488c888e.js",
"integrity": "sha256-5eWQEqhxp89EtL46B4vfVFzbxEGAFkVVFpYwFNDW+wI="
},
"rootRoutePath": "history"
}
А сам плагин на rsbuild:
/**
* Плагин для реализации схемы работы с microfronted-api, те этот файл создается при сборке, чтобы момент делпоя,он подтягиал этот файл
* @return
* {
* "remoteEntry": {
* "src": "/assets/remoteEntry.4217bdb1.js",
* "integrity": "sha256-s/ZVvpy3KpuuyCwdXCIAGUysOY4Gh0Zu05x8LJBJ8WI="
* },
* "rootRoutePath": "dashboard"
* }
*/
export const pluginCriticalAssets = (): RsbuildPlugin => ({
name: 'alfabank-newclick:plugin-critical-assets',
setup(api) {
if (builderConfig.enableCdn) {
api.onAfterBuild(({ stats, environments, isFirstCompile }) => {
if (isFirstCompile && stats?.toJson) {
const assets = stats.toJson({
all: false,
entrypoints: true,
}).children;
let criticalConfigEntry: CriticalFileRemoteEntry = {};
assets?.forEach((entryAssets) => {
const remoteEntry = entryAssets?.entrypoints?.[mfAppId]?.assets?.[0];
const platform = entryAssets.name;
if (platform && platform !== 'server' && remoteEntry) {
const jsName = remoteEntry.name;
const { distPath } = environments[platform];
criticalConfigEntry = {
src: `/assets/${jsName}`,
integrity: getFileIntegritySync(`${distPath}/${jsName}`),
};
}
});
writeAssetsFile(
JSON.stringify({
remoteEntry: criticalConfigEntry,
rootRoutePath: builderConfig.rootRoutePath,
}),
);
}
});
}
},
});
№ 2. В пайпе проекта берём этот файл и кладём в базу. После чего в хостом приложении мы получаем некоторую карту всей микрофронтендной среды.
Выглядит он примерно так:
{
"services": {
"newclick-stub-ui": {
"rootRoutePath": "stub",
"version": "0.0.215",
"remoteEntry": {
"src": "/assets/remoteEntr.fmvovp.js",
"integrity": "sha256-7s6H37/S2CiJgf7O+hfdrWzhIt6ekkAVxXhvkiiwBnI="
}
},
"newclick-dashboard-ui": {
"rootRoutePath": "dashboard",
"version": "0.0.525",
"remoteEntry": {
"src": "/assets/remoteEntry.488c888e.js",
"integrity": "sha256-5eWQEqhxp89EtL46B4vfVFzbxEGAFkVVFpYwFNDW+wI="
}
}
...
},
"domain": ""
}
№ 3. Таким же образом, как обычным путем с SRI, мы добаляем атрибут integrity
к внедряемым скриптам.
Вот так внедрив некоторые манипуляции в деплой UI-сервисов, мы получили общую карту приложения, что помогла с SRI, но и также расширила общее взаимодействие проектов.
№ 4. Что касается скриптов, которые загружает уже сам remoteEntry.js файл, то тут опять же помогают плагины для внедрения их в исполняемый код:
SRI является важным инструментом для повышения безопасности веб-приложений. Он защищает от атак, в которых злоумышленник может внедрить вредоносный код в сторонние ресурсы. Использование SRI особенно актуально в современном вебе, где проекты часто полагаются на множество внешних библиотек и фреймворков, размещённых на CDN.
Что касается микрофронтедов, то в реализации их появляются некоторые трудности, но все они решаются и в этой статье, надеюсь, удалось показать как сделали это мы
Читайте также: