Subresource Integrity: SriPlugin

Subresource Integrity (SRI) используется для проверки целостности загружаемых ресурсов, подключаемых через CDN или внешние источники. Механизм позволяет браузеру сравнить криптографический хеш загруженного файла с заранее вычисленным значением. При несовпадении ресурс блокируется, что предотвращает выполнение потенциально изменённого или скомпрометированного кода.

В экосистеме Webpack поддержка SRI реализуется через плагины, наиболее распространённый из которых — SriPlugin. Он интегрируется в процесс сборки и автоматически добавляет атрибуты integrity и crossorigin в HTML-выход, обычно совместно с HtmlWebpackPlugin.


Принцип работы Subresource Integrity

SRI основан на вычислении хеша содержимого ресурса с использованием криптографических алгоритмов (SHA-256, SHA-384, SHA-512). Итоговое значение кодируется в base64 и помещается в HTML-разметку:

<script
  src="https://cdn.example.com/app.js"
  integrity="sha384-Base64EncodedHash"
  crossorigin="anonymous">
</script>

При загрузке браузер:

  1. Получает файл по URL.
  2. Вычисляет хеш содержимого.
  3. Сравнивает с указанным integrity.
  4. Блокирует выполнение при несовпадении.

Роль Webpack в генерации SRI

Webpack сам по себе не управляет HTML-разметкой, однако через плагины может:

  • анализировать итоговые бандлы;
  • вычислять хеши файлов;
  • внедрять integrity-атрибуты;
  • синхронизировать SRI с кеш-бастингом (contenthash).

Основная задача SriPlugin — автоматизация вычисления SRI для всех выходных ассетов, подключаемых через HTML-шаблоны.


Архитектура SriPlugin

SriPlugin работает на этапе генерации HTML после завершения сборки Webpack:

  • получает список ассетов от compilation;
  • читает содержимое файлов из памяти emit assets;
  • вычисляет hash (SHA-384 чаще всего);
  • модифицирует HTML-теги скриптов и стилей;
  • добавляет integrity и crossorigin.

Типичная интеграция происходит вместе с HtmlWebpackPlugin.


Установка и базовая интеграция

SriPlugin подключается как обычный Webpack-плагин:

npm install sri-webpack-plugin --save-dev

Конфигурация Webpack:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const SriPlugin = require('sri-webpack-plugin');

module.exports = {
  output: {
    filename: '[name].[contenthash].js',
    publicPath: 'https://cdn.example.com/'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),

    new SriPlugin({
      hashFuncNames: ['sha384'],
      enabled: true
    })
  ]
};

Взаимодействие с HtmlWebpackPlugin

SriPlugin модифицирует HTML, уже сформированный HtmlWebpackPlugin. Последовательность выполнения критична:

  1. Webpack собирает бандлы.
  2. HtmlWebpackPlugin создаёт HTML с тегами <script> и <link>.
  3. SriPlugin вычисляет SRI и дополняет теги.

Результат:

<script
  src="main.8d31f.js"
  integrity="sha384-2n4kQx...Q0="
  crossorigin="anonymous">
</script>

Поддержка CSS и JavaScript

SriPlugin обрабатывает:

  • JavaScript чанки (<script>)
  • CSS файлы (<link rel="stylesheet">)

Пример CSS:

<link
  href="styles.3f91c.css"
  rel="stylesheet"
  integrity="sha384-AbC123..."
  crossorigin="anonymous">

Алгоритмы хеширования

Поддерживаются стандартные алгоритмы Web Crypto API:

  • sha256
  • sha384 (рекомендуется)
  • sha512

Конфигурация:

new SriPlugin({
  hashFuncNames: ['sha384', 'sha512']
});

При указании нескольких алгоритмов генерируются несколько значений integrity:

integrity="sha384-... sha512-..."

Совместимость с кешированием

SRI тесно связан с стратегией кеширования Webpack:

  • [contenthash] обеспечивает уникальность файла при изменении содержимого;
  • SRI гарантирует соответствие содержимого заявленному хешу.

Комбинация обеспечивает двойную защиту:

  • кеширование CDN;
  • контроль целостности на стороне клиента.

Работа с CDN

При использовании CDN SRI становится критически важным. Типичная схема:

  • статические ресурсы размещаются на CDN;
  • HTML отдаётся с основного сервера;
  • integrity фиксирует ожидаемое содержимое.

Пример конфигурации:

output: {
  publicPath: 'https://cdn.example.com/assets/'
}

SriPlugin автоматически подставляет integrity для всех ресурсов, загруженных через CDN.


Ограничения и особенности

Несмотря на автоматизацию, SRI имеет ряд особенностей:

  • любые изменения файла требуют пересборки HTML;
  • динамическая подмена скриптов ломает integrity;
  • инлайновые скрипты не поддерживаются;
  • SRI применяется только к внешним ресурсам.

Типичные ошибки интеграции

Несовпадение crossorigin

Без корректного crossorigin браузер может игнорировать SRI:

<script src="app.js" integrity="..." crossorigin="anonymous"></script>

Изменение содержимого после сборки

Любая постобработка CDN (minify, inject banner) делает хеш недействительным.

Несинхронизированные бандлы

При раздельной публикации HTML и JS возможна рассинхронизация:

  • HTML обновлён
  • JS файл ещё старый на CDN

Продвинутые настройки SriPlugin

Отключение для dev-сборки

new SriPlugin({
  enabled: process.env.NODE_ENV === 'production'
});

Фильтрация ресурсов

new SriPlugin({
  hashFuncNames: ['sha384'],
  assetMatch: /\.(js|css)$/
});

Кастомизация атрибутов

Некоторые версии плагина позволяют расширять HTML-атрибуты:

new SriPlugin({
  crossorigin: 'anonymous'
});

Интеграция с CSP (Content Security Policy)

SRI часто используется совместно с CSP:

Content-Security-Policy: script-src 'self' https://cdn.example.com; style-src 'self' https://cdn.example.com;

Сценарий защиты:

  • CSP ограничивает источники загрузки;
  • SRI контролирует содержимое ресурсов.

Комбинация снижает риск подмены CDN.


Сборка и вычисление хешей

SriPlugin использует потоковое чтение ассетов из compilation:

  • доступ к compilation.assets;
  • получение raw buffer;
  • вычисление digest через crypto API.

Псевдологика:

const hash = crypto
  .createHash('sha384')
  .update(assetSource)
  .digest('base64');

Использование с несколькими HTML страницами

При multi-page приложениях:

new HtmlWebpackPlugin({
  filename: 'admin.html',
  chunks: ['admin']
}),
new HtmlWebpackPlugin({
  filename: 'app.html',
  chunks: ['app']
}),
new SriPlugin()

SriPlugin обрабатывает каждый HTML отдельно, сохраняя соответствие чанков.


Производительность и влияние на билд

Влияние на сборку:

  • дополнительное чтение файлов из памяти;
  • вычисление криптографических хешей;
  • модификация HTML AST.

Обычно накладные расходы минимальны, но при большом количестве ассетов (сотни файлов) возможен рост времени генерации HTML.


Альтернативные подходы

Помимо SriPlugin используются:

  • интеграция через HtmlWebpackPlugin hooks;
  • ручная генерация integrity через Node.js скрипты;
  • внешние post-build инструменты.

Однако автоматизация через SriPlugin остаётся наиболее стабильным решением в Webpack-пайплайне.


Совместимость с современными версиями Webpack

SriPlugin работает поверх стандартных API Webpack 4 и Webpack 5:

  • Webpack 4: через compilation assets;
  • Webpack 5: через asset modules и enhanced hooks.

В Webpack 5 предпочтительно использование современных плагинов, поддерживающих async hooks.


Итоговая модель поведения SRI в Webpack

Процесс можно описать как последовательность:

  1. Сборка бандлов Webpack.
  2. Генерация HTML.
  3. Чтение итоговых ассетов.
  4. Вычисление криптографических хешей.
  5. Встраивание integrity и crossorigin.
  6. Финальный emit файлов.

Эта цепочка обеспечивает контроль целостности на уровне доставки фронтенд-ресурсов и делает возможным безопасное использование CDN без потери доверия к содержимому.