Понимание целостности подресурсов
Опубликовано: 2022-03-10 Если вы когда-либо использовали версию библиотеки JavaScript, размещенную на CDN, возможно, вы заметили странный атрибут integrity
в теге скрипта. Этот атрибут содержит, казалось бы, бесконечный буквенно-цифровой мусор, который у вас может возникнуть соблазн удалить в поисках более чистого кода.
Весь этот мусор на самом деле представляет собой действительно полезную функцию безопасности под названием Subresource Integrity (SRI), которая может помочь защитить ваш сайт от определенных типов взломов и компрометации. В этой статье мы рассмотрим, что такое SRI, как он может защитить вас и как вы можете начать использовать его в своих проектах, а не только для файлов, размещенных на CDN.
Немного истории
Давным-давно, когда JavaScript был гораздо более бедным родственником HTML и CSS, нам не нужно было слишком много думать о том, как наши скрипты могут быть использованы в качестве вектора атаки на наши веб-сайты. Большинство сайтов размещались на одном физическом сервере где-то в нашей собственной хостинговой инфраструктуре, и именно этот сервер мы думали о защите, когда речь шла о передовых методах обеспечения безопасности.
По мере того, как браузеры становились все более функциональными, а сетевые подключения становились все толще, мы стали использовать все больше и больше JavaScript, и, в конце концов, начали появляться многоразовые библиотеки JavaScript. В те первые дни такие библиотеки, как script.aculo.us, Prototype и, в конечном итоге, jQuery, начали получать признание среди разработчиков, стремящихся добавить больше интерактивности на свои страницы.
С этими добавленными библиотеками и последующими плагинами увеличился вес страницы, и вскоре мы начали серьезно думать о производительности внешнего интерфейса. Такие ресурсы, как сети доставки контента (CDN), которые ранее были резервом гигантских корпораций, стали обычным явлением для повседневного создания быстрых веб-сайтов.
Попутно какая-то светлая искра заметила, что все сайты запрашивают свои собственные копии общих библиотек — такие вещи, как последняя версия jQuery — и если бы существовала общая версия этих библиотек CDN, которую мог бы использовать каждый сайт, то пользователь не стал бы не нужно продолжать скачивать один и тот же файл. Они принимали удар за первый сайт, который использовал файл, но затем он оставался в их локальном кеше браузера, и загрузки для каждого последующего сайта могли быть пропущены. Гений!
Вот почему вы увидите ссылки CDN на ваши любимые библиотеки с использованием таких URL-адресов, как jsdelivr.com
— они используют общий CDN для размещения файлов, чтобы их пользователи видели преимущества производительности.
Что может пойти не так?
Это остается хорошим практичным способом работы, но он создает потенциальный вектор для атаки. Давайте представим, что сейчас 2012 год, и все используют новый jQuery 1.8. Возвращаясь к традиционному способу ведения дел, у каждого будет свой собственный файл jQuery 1.8, размещенный как часть их собственного веб-сайта на их собственном сервере.
Если бы вы были каким-то злодеем — например, каким-то гамбургером на основе jQuery — и придумали хитрый способ взломать библиотеку для собственной злой выгоды, вам пришлось бы нацеливаться на каждый веб-сайт в отдельности и скомпрометировать их серверы, чтобы иметь любое воздействие. Это много усилий.
Но сейчас все не так, поскольку все используют jQuery, загружаемый с общего CDN. И когда я говорю всем, я не имею в виду сотни веб-страниц. Я имею в виду миллионы веб-страниц. Внезапно этот файл стал очень привлекательной целью для нашего теневого хакера. Если они смогут скомпрометировать этот единственный файл, они смогут очень быстро запустить код на миллионах веб-страниц по всему миру.
Неважно, что это за код. Это может быть розыгрыш для порчи страниц, это может быть код для кражи ваших паролей, это может быть код для майнинга криптовалюты, или это могут быть хитрые трекеры, которые следят за вами в Интернете и создают маркетинговый профиль. Важно то, что невинный файл, добавленный разработчиком на страницу, был изменен, и теперь у вас есть какой-то вредоносный JavaScript, работающий как часть вашего сайта. Это большая проблема.
Введите целостность подресурса
Вместо того, чтобы откатывать часы назад и отказываться от полезного способа использования кода, SRI — это решение, которое добавляет простой уровень безопасности. Что делает SRI и атрибут integrity
, так это гарантирует, что файл, который вы связали со страницей, никогда не изменится. И если он изменится, то браузер его отклонит.
Проверка того, что код не изменился, — очень старая проблема в информатике, и, к счастью, у нее есть несколько очень хорошо зарекомендовавших себя решений. SRI хорошо справляется с самым простым — хэшированием файлов.
Хеширование файлов — это процесс взятия файла и его обработки с помощью алгоритма, который сводит его к короткому строковому представлению, известному как хэш или контрольная сумма. Не вдаваясь в подробности, процесс либо повторяем, либо обратим, настолько, что если вы дадите кому-то файл вместе с хэшем, он сможет запустить тот же алгоритм, чтобы проверить, совпадают ли они. Если файл изменится или изменится хэш, совпадения больше не будет, и вы поймете, что что-то не так, и не должны доверять файлу.
При использовании SRI ваша веб-страница содержит хэш, а сервер (CDN или где-либо еще) содержит файл. Браузер загружает файл, а затем быстро вычисляет, чтобы убедиться, что он совпадает с хешем в атрибуте integrity
. Если он совпадает, то файл используется, а если нет, то блокируется.
Пробуем
Если я зайду сегодня на getbootstrap.com
, чтобы получить ссылку CDN на версию Bootstrap, мне дадут тег, который выглядит так:
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
Вы можете видеть, что атрибут src
такой, к которому мы привыкли, а атрибут integrity
содержит то, что мы теперь знаем как хэш.
Хэш на самом деле состоит из двух частей. Первый — это префикс для объявления используемого алгоритма хеширования. В данном случае это sha384
. Затем следует тире, а затем сам хеш, закодированный с помощью base64
.
Возможно, вы знакомы с base64
как со способом кодирования встроенных файлов, таких как изображения, в страницы. Это не криптографический процесс — это просто быстрый и удобный способ закодировать потенциально беспорядочные данные таким образом, чтобы они аккуратно транслировались в ASCII. Вот почему он так часто используется в Интернете.
Увидев это, браузер загрузит bootstrap.min.js
. Перед его выполнением он декодирует хэш base64
, а затем использует алгоритм хеширования sha384
, чтобы подтвердить, что хэш соответствует только что загруженному файлу. Если он совпадает, файл выполняется.
Я могу проверить это, поместив этот тег на страницу, а затем перейдя на вкладку «Сеть» в инструментах браузера, чтобы увидеть, что файл был загружен.
Я вижу, что bootstrap.min.js
(а также необходимый файл jQuery) успешно загружены.
Давайте посмотрим, что произойдет, если я обновлю хеш, чтобы он стал чем-то, заведомо неверным.
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-SmashingMagazineIsCoolForCats" crossorigin="anonymous"></script>
Как видите, хэш, указанный на моей странице, больше не соответствует файлу, поэтому файл блокируется.
Использование SRI в ваших собственных проектах
Наличие этой возможности для библиотек в CDN — это здорово, и если вы видите вариант использования встроенного файла с атрибутом integrity
, вам определенно следует отдать предпочтение этому варианту. Но это не ограничивается большими проектами на CDN, вы можете использовать это самостоятельно для своих собственных сайтов.
Нетрудно представить сценарий, в котором хакеру удается получить доступ всего к нескольким файлам на вашем сайте. Я думаю, что большинство из нас видели клиента, коллегу или друга, у которого в какой-то момент был скомпрометирован сайт WordPress с кучей неприятного мусора, о котором они даже не подозревали.
SRI может защитить вас и от этого. Если вы создаете хэши целостности для своих собственных файлов, ваш сайт может отклонять любые изменения так же, как и для удаленно размещенного файла.
Генерация хэшей
Как и следовало ожидать, вы можете запустить некоторые команды на терминале вашего компьютера, чтобы сгенерировать хэш для файла. Этот пример того, как это сделать, взят со страницы MDN Subresource Integrity:
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
Это получение содержимого FILENAME.js
и передача его в качестве входных данных в openssl
для создания дайджеста с использованием sha384
, который затем передается в качестве входных данных в другую команду openssl
для кодирования результата в base64
. Это не только сложно и неясно, но и не то, что вы хотите делать вручную каждый раз, когда ваш файл JavaScript изменяется.
Что еще более полезно, вы захотите каким-то образом интегрировать это в процесс сборки вашего сайта, и, как вы понимаете, там есть множество готовых вариантов. Точная реализация будет сильно различаться в зависимости от вашего проекта, но вот некоторые строительные блоки.
Если вы используете Gulp для создания своих сайтов, есть gulp-sri, который выводит файл JSON со списком ваших файлов и их хэшей. Затем вы можете использовать это на своем сайте. Например, для динамически отображаемого сайта вы можете создать подключаемый модуль шаблона для чтения этого файла и добавления хэшей в свои шаблоны, где это необходимо.
Если вы все еще пользуетесь Gulp, но у вас есть статический сайт (или статически сгенерированный сайт), вы можете использовать gulp-sri-hash, который фактически будет запускать ваши HTML-страницы и изменять страницы, добавляя хэши там, где это необходимо, что очень удобно.
Если вы используете Webpack, есть функция webpage-subresource-integrity, которая в истинном стиле Webpack сложнее, чем можно было бы ожидать от человека, но, похоже, работает.
Для тех, кто использует механизм шаблонов Handlebars, вам, похоже, доступны варианты, и если ваш процесс сборки представляет собой просто базовый JavaScript, там тоже есть простые решения.
Если вы используете CMS, например WordPress, я нашел плагин, который, похоже, упрощает эту задачу, хотя сам я его не пробовал. Поиск в Google выбранной вами платформы с помощью SRI или Sub Resource Integrity, скорее всего, укажет вам правильное направление.
По сути, вы хотите подключить свое хеширование после того , как ваши файлы JavaScript были минимизированы, а затем сделать этот хеш каким-то образом доступным для любой части вашей системы, выводящей теги <script>
. Одним из чудес веб-платформы является то, что она настолько разнообразна с технической точки зрения, что, к сожалению, не позволяет мне дать вам хорошие инструкции по реализации!
Другие вещи, которые следует отметить
В этой статье я много говорил о файлах JavaScript, поскольку именно в них наиболее целесообразно защищаться от хакерских атак. SRI также работает с CSS, так что вы можете использовать его там точно так же. Риск вредоносного CSS намного ниже, но возможность испортить сайт все еще существует, и кто знает, какие ошибки браузера также могут привести к тому, что CSS непреднамеренно откроет ваш сайт для хакера. Так что это тоже работа с использованием SRI.
Еще одна интересная вещь, которую вы можете сделать, — это использовать Content Security Policy, чтобы указать, что любой сценарий (или стили) на вашей странице должен использовать SRI, и, конечно же, этот SRI должен проверяться.
Content-Security-Policy: require-sri-for script;
Это способ гарантировать, что SRI всегда используется, что может быть полезно на сайтах, над которыми работают несколько членов команды, которые могут или не могут быть полностью в курсе того, как делать что-то. Опять же, хорошим местом, чтобы узнать больше об этом, является всегда отличная документация MDN для целостности подресурсов.
Последнее, о чем стоит поговорить, это поддержка SRI браузерами. Поддержка в современных браузерах широка, за исключением Internet Explorer. Тем не менее, благодаря обратно совместимому способу реализации спецификации ее можно безопасно использовать сразу. Браузеры, понимающие атрибут integrity
, будут использовать хэш и проверять целостность, а старые браузеры будут продолжать работать как обычно. Конечно, вы не получите дополнительной защиты в этих старых браузерах, но вы получите ее в браузерах, которые предлагают поддержку.
Заключение
Мы видели не только то, что делают эти странные хэши в атрибутах integrity
, но и то, как мы можем использовать их для защиты от определенных типов атак на наш веб-сайт. Конечно, не существует единственной серебряной пули, которая защитит наши сайты от всех типов эксплойтов, но целостность подресурсов — действительно полезный инструмент в цепочке.
Использование уязвимости в системе безопасности часто сводится к тому, чтобы выровнять несколько мелких элементов. Если A существует, и вы можете сделать B возможным, то ошибка в C делает возможным D. Функции браузера, такие как SRI, дают нам хороший способ связать вещи еще немного и потенциально разорвать эту цепочку и помешать хакеру получить то, что он хочет. Более того, если вы можете интегрировать его в свой процесс сборки или CMS, вы сможете настроить его один раз, а затем забыть об этом, и это не будет доставлять вам повседневных неудобств.
Таким образом, я настоятельно рекомендую серьезно взглянуть на целостность субресурсов и внедрить ее на свои сайты, если сможете.