Первая линия обороны от XSS - Sanitization
Last updated
Last updated
После обсуждения различных основ и техник атак XSS давайте поговорим о защите.
Как упоминалось ранее при обсуждении защиты от XSS, мы можем кодировать пользовательский ввод, чтобы предотвратить его интерпретацию в его исходном значении, тем самым избегая рисков.
В дополнение к кодированию существует еще один метод, который больше похож на "удаление опасных частей из пользовательского ввода". Эта операция называется санитизацией, а программа, отвечающая за ее обработку, обычно называется санитайзером.
Существуют некоторые тонкие различия между этим и ранее упомянутыми "кодированием" или "экранированием". Кодировка кодирует только определенные символы в пользовательском вводе, которые по-прежнему будут отображаться как обычный текст. Санитизация, с другой стороны, удаляет всю часть, которая не соответствует правилам, полностью ее удаляя.
Прежде чем перейти к основной теме, давайте посмотрим на ответ на предыдущий пост. В конце предыдущего поста я опубликовал небольшой код и спросил, есть ли у кого-то вопросы:
Проблема в этом коде заключается в том, что проверка URL недостаточно строгая, позволяющая пользователям вводить ссылки на видео, такие как , и такая ссылка приведет к странице настроек аккаунта.
Но это не звучит слишком плохо, верно? Это просто другая страница YouTube, и она все еще должна быть в рамках YouTube.
В теории это верно, если только на сайте нет уязвимости , которая может перенаправить на любой URL. В этом случае злоумышленник может контролировать содержимое, отображаемое в iframe.
Например, если перенаправляет на , то я могу использовать этот URL, чтобы в iframe отобразился мой блог, а не ожидаемое видео с YouTube.
И действительно, на YouTube в настоящее время есть URL с open redirect, которые можно использовать. Однако, поскольку они могут быть исправлены в любое время, я не буду предоставлять здесь эти URL.
Если вам нужно проверить URL, рекомендуемый подход - использовать new URL() для разбора и смотреть на возвращаемое значение. Этот метод намного надежнее, чем простое сравнение строк или RegExp.
Почему работает атака XSS?
Потому что инженеры ожидают, что ввод пользователя будет простым текстовым вводом, но на самом деле этот ввод интерпретируется браузером как часть HTML-кода. Это и создает уязвимость. Оно похоже на инъекцию SQL, когда сервер ожидает, что вы вводите строку, но она интерпретируется как часть команды SQL.
Таким образом, решение простое: кодировать ввод пользователя и заставить его выглядеть так, как он должен выглядеть.
В разработке интерфейсов при выводе ввода пользователя на экран в JavaScript следует использовать innerText или textContent вместо innerHTML. Таким образом, ввод пользователя будет интерпретироваться как обычный текст.
React и Vue имеют встроенные аналогичные функции. Основная логика:
> По умолчанию все, что отображается, будет восприниматься как обычный текст. Если требуется отобразить HTML, используйте специальные методы, такие как dangerouslySetInnerHTML или v-html.
Однако в наши дни многие бэкэнды непосредственно сами не выводят контент. Для этого они используют шаблонизаторы. Например, широко используемые handlebarsjs по умолчанию считают {{ name }} закодированным выводом. Для вывода необработанного содержимого требуются три фигурные скобки: {{{ vulnerable_to_xss }}}.
В шаблонизаторе Laravel, {{ $text }} кодируется, а {!! $text !!} - нет. Интересно, служит ли знак восклицания предупреждением: "Эй, будьте осторожны при использовании этого".
Некоторые шаблонизаторы используют фильтры. Например, в Python Jinja, {{ text }} кодируется, в то время как {{ text | safe }} означает, что содержимое безопасно и может быть непосредственно выведено в исходном формате.
Таким образом, при написании кода лучше всего использовать безопасный подход по умолчанию. Обратите внимание на небезопасные части (включая упомянутую ранее проблему с <a href>, которая требует особого внимания).
Когда мы будем использовать небезопасные методы вывода?
Обычно это происходит, когда исходный текст уже находится в формате HTML. Например, платформы для блогов могут поддерживать определенные HTML-теги, что является обычным сценарием.
Так как мы работаем с этой ситуацией? Здесь вступает в игру санитизация.
Как говорится, используйте библиотеку, которую уже построил кто-то другой, а не пытайтесь изобрести колесо снова.
Если используемый вами фреймворк или язык программирования уже предоставляет соответствующую функциональность, используйте ее. Если нет, найдите библиотеки с хорошей репутацией, которые широко используются. Разумеется, эти библиотеки могут иметь уязвимости, это неизбежно, но обычно они принимают во внимание множество сценариев и решают многие проблемы, поэтому они гораздо безопаснее, чем то, что вы сможете сделать самостоятельно.
Кроме того, эти библиотеки должны быть специально разработаны для достижения целей санитизации; в противном случае это будет работать, как если бы вы делали это самостоятельно.
Например, Python имеет библиотеку под названием BeautifulSoup, которая может разбирать веб-страницы и обычно используется для веб-скрейпинга. Однако она не предназначена для санитизации, поэтому ее использование может вызвать проблемы.
Хоть она не предназначена для санитизации, она используется для разбора веб-страниц, верно? Так почему мы не можем использовать ее?
Позвольте продемонстрировать:
Вывод этой программы:
Это выглядит нормально, поскольку верно распознаются имена тегов и атрибуты. Так что, не могу ли я просто создать разрешенный или заблокированный список самостоятельно? Это звучит разумно, но на самом деле...
Выходные данные:
Выглядит нормально, но если вы откроете вышеуказанный HTML в браузере, вы увидите наше любимое всплывающее окно, указывающее, что JavaScript был выполнен, и проверку BeautifulSoup успешно обошли.
Обход основан на различиях в разборе между браузерами и BeautifulSoup для следующих HTML:
Парсер HTML BeautifulSoup рассматривает это как комментарий, заключенный в <!-- и -->, поэтому он не будет разбирать ни теги, ни атрибуты.
Используя это различие при разборе, злоумышленник может обойти проверку и успешно выполнить XSS.
Кстати, если вы переключите парсер BeautifulSoup на lxml, он все равно не сможет правильно его разобрать. Но если вы переключитесь на html5lib, то он правильно разберет его как <script>. Однако, с html5lib могут возникнуть другие проблемы.
Так что, есть ли какие-нибудь рекомендуемые библиотеки, специально разработанные для санитизации? Да, мне как раз известна одна.
Базовое использование DOMPurify выглядит так:
Он делает многое вне поля нашего зрения и не только удаляет опасные теги и атрибуты, но и защищает от других атак, таких как DOM-Clobbering. Он очень тщательно работает.
DOMPurify по умолчанию разрешает только безопасные теги, такие как <h1>, <p>, <div> и <span>. Он также удаляет все обработчики событий и очищает ранее упомянутый псевдо-протокол javascript:, гарантируя, что любой ваш HTML-ввод не приведет к XSS в стандартном сценарии.
Но стоит отметить, что тег <style> включен по умолчанию, и мы обсудим связанные с ним риски позже.
Если вы хотите разрешить больше тегов или атрибутов, вы можете настроить соответствующие параметры:
Из приведенного выше примера видно, что даже если мы разрешаем атрибут src тега iframe, опасное содержимое все равно будет автоматически отфильтровано. Это связано с тем, что мы разрешаем только атрибут src и не разрешаем использование javascript:.
Однако, если вы хотите разрешить некоторые атрибуты или теги, которые могут вызвать XSS, DOMPurify вас не остановит:
При использовании этих библиотек важно научиться использовать их посредством официальной документации и уделять дополнительное внимание во время использования. Даже с правильной библиотекой, неверные конфигурации все равно могут вызвать проблемы.
Первый классический случай - это уязвимость, обнаруженная известным тайваньским хакером, апельсином, в 2019 году. При фильтрации контента HackMD использовал следующую конфигурацию (HackMD использует другой пакет под названием js-xss):
Если тег равен !--, он прямо игнорируется и не возвращается. Намерение было сохранить комментарии, например, <!-- Hello--> будет рассматриваться как тег с именем !--.
Однако, Orange обошел это следующим образом:
Поскольку <!-- рассматривается как тег, содержимое выше просто добавляет атрибут foo. Но когда браузер выполняет его рендеринг, открывающий <!-- сочетается с bar--> в foo, становясь комментарием HTML, и следующий <s>Hi</s> отображается, в результате возникает уязвимость XSS.
Я также обнаружил другой случай в 2021 году, но это все равно было неправильное использование.
Веб-сайт на бэкенде санитизировал article.content, и frontend-rendering был написан следующим образом:
Уже отфильтрованный контент прошел обработку optimizeEmbed, что означает, что если возникает проблема с optimizeEmbed, она все равно может вызвать XSS.
Давайте посмотрим, что делает эта функция (некоторый код отсутствует):
Здесь URL изображения напрямую соединяется в строку, и атрибуты не заключены в одиночные или двойные кавычки! Если мы можем контролировать toSizedImageURL, мы можем проэксплуатировать уязвимость XSS. Реализация этой функции следующая:
Если URL не соответствует указанным условиям, он возвращается непосредственно; в противном случае перед возвращением выполняется некоторая обработка строк. В итоге, мы действительно можем контролировать возвращаемый этой функцией результат.
Используя style=animation-name:spinning вместе с обработчиком событий onanimationstart=console.log(1337), уязвимость XSS успешно создается без необходимости взаимодействия с пользователем.
Из двух представленных выше случаев мы видим, что: 1. Используется неправильная конфигурация 2. Изменение содержимого после фильтрации
Эти факторы могут вызвать проблемы и привести к уязвимостям XSS.
Таким образом, даже с правильной библиотекой, важно обращать внимание на способ ее использования. Малейшая ошибка все равно может привести к уязвимости XSS.
В этой статье мы поговорили о первом способе защиты от XSS, который заключается в кодировании или санитизации пользовательских данных для удаления опасного контента или безопасного его отображения на экране.
Это звучит просто, но на самом деле довольно таки трудно, иначе не было бы столько уязвимостей XSS.
Что касается backend, в PHP вы можете использовать функцию . В документации показано, какие символы она кодирует:
Однако, согласно спецификации - <!--> является допустимым пустым комментарием, поэтому вышеупомянутый код становится комментарием, за которым следуют тег <script> и текст -->.
- это открытый пакет, разработанный немецкой компанией по кибербезопасности Cure53, специально для санитизации HTML. В Cure53 много специалистов, которые специализируются на веб- и фронтенд-разработке и сообщили о многих известных уязвимостях. Они эксперты в этой области.
Документация DOMPurify достаточно подробна, и есть специальная страница под названием "", которая объясняет цели этой библиотеки и ситуации, в которых могут возникнуть проблемы.
Для получения более подробной информации и исправления, пожалуйста, обратитесь к оригинальной статье:
Если переданный URL равен style=animation-name:spinning onanimationstart=alert(1337), конкатенированный HTML будет:
Дополнительные ссылки: 1.