Сторонние скрипты — это риск цепочки поставок
Сторонний скрипт — это код, который вы не писали, который отдаёт компания, которую вы не контролируете, и который выполняется внутри ваших страниц с сессиями ваших пользователей: в терминах цепочки поставок это зависимость, которая может изменить свой собственный код в любой момент — без выпуска, без ревью и без уведомления. В октябре 2026 года этот абстрактный риск стал, как сообщалось, конкретной потерей, когда скрипт мониторинга производительности, загружаемый крупным торговым сайтом, был подменён на построенный для выкачивания его пользователей. Эта статья проходит по тому, что было сообщено, что Subresource Integrity на самом деле останавливает, почему закрепление версии предшествует её хэшированию, когда самостоятельный хостинг — лучший ответ, и короткую процедуру аудита, которая держит опись честной.
Что произошло в Polymarket, как сообщалось
29 октября 2026 года, как сообщалось, международный сайт Polymarket перевёл торговлю в режим только отмен после того, как пользователи сообщили, что средства уходят с их счетов, и торговля возобновилась примерно через час. Собственные заявления компании, как сообщалось, отнесли инцидент на счёт скомпрометированного стороннего поставщика, сказали, что дело передано правоохранительным органам, и пообещали полностью возместить пострадавшим пользователям. Отчёты, основанные на постмортеме компании от 30 октября, оценили взятую в атаке сумму примерно в 2,7 миллиона долларов, с общими возмещениями в 3,9 миллиона, как только были включены доброжелательные выплаты за потери, вызванные паузой в торговле; независимые ончейн-анализы, цитируемые в освещении, проследили похожую цифру, около 2,7 миллиона, распределённую по нескольким сотням кошельков. Ни одна из этих цифр не проверена здесь независимо — это цифры репортажей, и эта статья обращается с ними именно так.
Механизм, как сообщалось, — та часть, которая обобщается. Исследователи, цитируемые в освещении, связали вредоносный код со скриптом мониторинга реальных пользователей (RUM) поставщика веб-производительности — маленьким файлом, чья вся работа — измерять скорость загрузки страниц в браузерах посетителей, встраиваемым по клиентам с CDN поставщика. Этот файл, как сообщалось, был заменён полезной нагрузкой, написанной специально для целевого сайта: она работала внутри вошедших сессий, использовала собственную торговую машинерию сайта, уже присутствующую в странице, и перемещала позиции и средства в контролируемое атакующим место назначения. Как сообщалось, ни недостаток смарт-контракта, ни украденные пароли или приватные ключи не были замешаны — подменённый скрипт просто был страницей, с точки зрения браузера. Сможет ли каждая деталь этого рассказа выдержать позднейшую проверку, для урока не по существу: форма атаки — один файл поставщика, подменённый один раз, выполненный каждым посетителем — является стандартной формой этого класса инцидентов.
Сторонний скрипт — это ваш код, с чужим процессом выпуска
В классическом теге script нет песочницы. Код, загруженный с CDN поставщика, выполняется в вашем оригинале с тем же доступом, что и ваш собственный бандл: он может читать и менять DOM, читать значения форм по мере ввода, вызывать ваши API с сессией пользователя и отправлять найденное куда угодно. Руководство OWASP по стороннему JavaScript называет три риска прямо — утрата контроля над изменениями клиентского приложения, выполнение произвольного кода на клиентских системах и раскрытие чувствительной информации третьим сторонам — и все три следуют из этого одного факта. Ваш процесс ревью, ваша заморозка кода и ваш комитет по изменениям заканчиваются на вашей собственной сборке; файл поставщика может отредактировать поставщик, атакующий, скомпрометировавший поставщика, или тот, кто перерегистрирует домен поставщика, если он когда-нибудь истечёт.
Эта асимметрия — причина, по которой «мы доверяем этому поставщику» не является контролем. Доверие решает, чей скрипт вы принимаете сегодня; оно ничего не говорит о том, что этот URL будет отдавать завтра, и оно не даёт вам никакого сигнала, когда содержимое меняется. Контроли, которые работают, — это те, что либо устраняют доверие (отдавать файл самому), либо ограничивают его (Content-Security-Policy, которая ограничивает, что любой скрипт может делать и куда может отправлять данные), либо механически проверяют его при каждой загрузке — для чего и существует Subresource Integrity. Начните с описи: вы не можете применить ни один из трёх к скриптам, о которых не знаете, что они у вас есть, а у большинства сайтов их больше, чем ожидают их владельцы, как только посчитаны маркетинговые теги, виджеты поддержки и встраиваемый мониторинг.
Что SRI останавливает — и чего не останавливает
Subresource Integrity — это механизм браузера, специфицированный W3C и документированный MDN: страница заявляет криптографический хэш файла, который она ожидает, в атрибуте integrity тега, браузер хэширует то, что он на самом деле скачал, и при любом несовпадении отказывается выполнять файл. Для межоригинного файла тегу также нужен crossorigin="anonymous", а сервер должен отправлять заголовки CORS, чтобы браузеру было позволено выполнить проверку. Применённая к фиксированной библиотеке, SRI превращает тихую подмену в громкий отказ: прибывший файл либо байт в байт является файлом, который вы ревьюили, либо вообще не запускается. Если бы скрипт мониторинга в сообщённом инциденте был фиксированным файлом, загруженным таким образом, подменённая нагрузка провалила бы проверку вместо того, чтобы выполняться в сессиях пользователей — это условное наклонение является честной формой утверждения, потому что был ли тот конкретный встраиваемый код хэшируемым — вопрос о динамическом файле по клиентам, что и есть ровно трудный случай ниже.
Что SRI не останавливает, столь же важно. Он не помогает скриптам, чьё содержимое должно меняться, — контейнерам менеджеров тегов, загрузчикам персонализации и согласия, файлам, генерируемым по клиентам, — потому что нет стабильного хэша, чтобы закрепить. Он не судит файл, который вы хэшировали: верно доставленный скрипт по замыслу может всё равно быть любопытным, небрежным или вредоносным, и SRI будет вечно радостно его верифицировать. Он не покрывает скрипты, внедрённые во время выполнения другими скриптами, не защищает собственные файлы, отдаваемые с вашей собственной инфраструктуры, и ничего не делает с данными, которые законный скрипт собирает, как только запустится. SRI — это одна механическая гарантия — именно эти байты, или ничего — и он заслуживает своё место рядом с CSP, ревью поставщиков и минимизацией того, сколько третьих сторон получают доступ уровня сессии с самого начала, а не вместо них. Проверка заголовков безопасности этого сайта оценивает сторону CSP этой пары.
Закрепите версию, иначе хэш не устоит
У SRI есть предусловие, на котором проваливается больше аудитов, чем на злой воле: URL должен всегда возвращать одни и те же байты. Хэш, закреплённый поверх адреса latest, плавающей главной версии или файла по клиентам, который поставщик регенерирует, — это хэш, который ломается при первом же обновлении поставщика, — а команды, которых однажды разбудил собственноручно вызванный сбой SRI, склонны удалить атрибут вместо того, чтобы починить адрес, оставляя страницу хуже, чем неизмеренную. Прочный порядок обратен: сначала измените URL так, чтобы он называл точную версию ([email protected], /v2.4.0/), отревьюйте эту версию один раз, а затем хэшируйте именно этот файл. Повышения версий тогда становятся намеренными событиями — новый URL, новый хэш, один коммит — вместо окружающих изменений, о которых вы узнаёте от своих пользователей.
Закрепление имеет цену, и честность требует её назвать: закреплённая библиотека больше не получает тихих исправлений безопасности от поставщика, поэтому закрепление создаёт обязанность отслеживать выпуски и повышать намеренно — ту же обязанность, что несёт любая вендорная зависимость. Это управляемая обязанность — заметки о выпуске и календарное напоминание её покрывают — и она строго лучше альтернативы, которую заменяет, в которой обновления прибывают без ревью в темпе поставщика, а поломки прибывают без объявления в вашем. Относитесь к паре как к одной практике: закрепление делает содержимое адресуемым, хэш делает его проверяемым, а аудит следит, чтобы ни то, ни другое тихо не сгнило, — строка версии, выредактированная миграцией CMS, столь же обычна, как отсутствующий хэш, и лишь немного труднее замечается глазом.
Самостоятельный хостинг и процедура аудита
Для скриптов, которые значат больше всего, есть более прямой вариант: отдавайте файл сами. Самостоятельный хостинг полностью убирает сервер, аккаунт и домен поставщика из вашей цепочки поставок времени выполнения, позволяет вашему обычному конвейеру развёртывания ревьюить и хэшировать файл и продолжает работать, когда CDN поставщика не работает. Компромиссы реальны — вы отказываетесь от краевого кэша поставщика и автоматических обновлений, вы берёте на себя обязанность доставлять его исправления безопасности сами, а лицензии некоторых поставщиков или их файлы, генерируемые по клиентам, просто этого не позволяют, — поэтому разумное деление идёт по критичности: самостоятельно хостите маленькие фиксированные библиотеки на чувствительных страницах; закрепляйте и хэшируйте то, что остаётся на CDN; управляйте (ограничениями публикации, ревью изменений и CSP) динамическими тегами, которые нельзя ни закрепить, ни захостить; и удаляйте с глаз долой любой скрипт, который никто не может обосновать. Каждый удалённый скрипт — это цепочка поставок, которой у вас больше нет.
Процедура, которая держит это истинным, достаточно коротка, чтобы её действительно выполнять. Ежеквартально — и после каждого редизайна, миграции CMS или нового поставщика — запускайте аудит сторонних скриптов и SRI по вашим ключевым шаблонам страниц: он перечисляет каждый скрипт, на который ссылается статический HTML, какие из них сторонние, каким не хватает целостности и какие не закреплены, и вычисляет готовый к вставке sha384 для первых недостающих хэшей, которые сможет получить. Исправляйте в порядке воздействия — страницы сессий, платежей и персональных данных сначала, — затем проверьте транспортный и заголовочный слои с помощью оценщика SSL и проверки заголовков, и закончите в менеджере тегов, чьи внедрённые во время выполнения скрипты статический аудит не может видеть: ревьюйте его список публикаторов и историю изменений с тем же подозрением, что вы дали бы журналу развёртываний. Пятнадцать минут в квартал — против класса инцидентов, чья сообщённая стоимость в случае выше за примерно час ушла в миллионы.