Опасный псевдопротокол javascript
В предыдущей части обсуждались различные сценарии XSS и способы выполнения вредоносного кода, включая псевдопротокол javascript:. Даже с точки зрения современного фронтенд-разработчика, это то, о чем нужно быть особенно осведомленным.
Поэтому стоит посвятить этому отдельную часть для подробного рассмотрения.
Прежде чем начать, давайте ответим на вопрос из прошлой части.
В точке внедрения innerHTML теги <script> не выполняются. Однако их можно использовать совместно с iframe.
Атрибут srcdoc iframe может содержать полный HTML-код, создавая новую веб-страницу. Таким образом, ранее бесполезный тег <script> можно использовать здесь. Кроме того, поскольку это атрибут, его содержимое можно предварительно закодировать, что означает, что результат будет таким же:
Таким образом, даже если точка внедрения - innerHTML, комбинация <iframe srcdoc> и <script> может использоваться для выполнения кода.
Что такое псевдопротокол javascript:?
Слово "псевдо" относится к псевдокоду, который похож на виртуальный код.
По сравнению с "реальными протоколами" типа HTTP, HTTPS или FTP, псевдопротокол больше похож на специальный протокол, не связанный с сетью. Примеры псевдопротоколов включают mailto: и tel:.
Псевдопротокол javascript: особенен тем, что его можно использовать для выполнения JavaScript-кода.
Где можно использовать псевдопротокол javascript:?
Первое место - атрибут href, упомянутый в предыдущей части
Простое нажатие на эту ссылку вызовет JavaScript и XSS срабатывает.
Второе место - атрибут src в <iframe>:
В отличие от примера с <a>, для его срабатывания не требуется никакого взаимодействия с пользователем.
Наконец, атрибут action в <form> также может содержать то же самое. Атрибут formaction в <button> также аналогичен, но оба, как и <a>, требуют нажатия для запуска:
Почему это опасно?
Псевдопротокол javascript: часто упускают из виду, хотя на практике его используют довольно часто.
Например, imagine website с функцией вставки видео с YouTube по ссылке, которую вводит пользователь. Если разработчик этой функции не думает о безопасности, он может написать ее так:
Правильный подход - проверить, соответствует ли URL формату видеоролика YouTube и убедиться, что он начинается с https://.
Думаете, такая функция встречается редко? Попробуйте ввести на странице Facebook ссылку на собственный ресурс. Такие функции встречаются чаще, чем кажется, верно?
Если реализация на бэкенде написана на коде, это может выглядеть так:
Хотя символы <>" и закодированы, что предотвращает добавление тегов и выход из кавычек для добавления атрибутов, злоумышленник все равно может внедрить javascript:alert(1), поскольку в нем нет запрещенных символов.
Кроме того, современные фреймворки фронтенда обычно автоматически обрабатывают экранирование. Если вы не используете dangerouslySetInnerHTML в React или v-html в Vue, проблем быть не должно. Однако с href все по-другому по причинам, упомянутым выше - его содержимое не очищается.
Поэтому, если вы напишете в React так, это приведет к проблемам:
Это уязвимость XSS, где выполнение кода всего в один клик.
Однако React ввел предупреждение об этом поведении в версии 16.9, и оно также задокументировано:
В сообщении о предупреждении говорится:
Будущая версия React заблокирует URL javascript: в качестве меры безопасности. Если возможно, используйте обработчики событий. Если вам нужно генерировать небезопасный HTML, попробуйте использовать dangerouslySetInnerHTML.
Будьте осторожны с псевдопротоколом javascript: и всегда думайте о потенциальных уязвимостях XSS при работе с пользовательским вводом. Используйте современные фреймворки и библиотеки, которые помогают безопасно обрабатывать ввод, и следуйте рекомендациям по написанию безопасного кода.
В этом разделе рассмотрим еще несколько аспектов опасности псевдопротокола javascript:
В Vue можно написать код, подобный этому:
Перенаправление страниц также несет в себе риски
Многие сайты реализуют функцию "перенаправление после входа", которая перенаправляет пользователей на изначально запланированную страницу. Например:
В чем проблема с этим кодом?
Значение window.location также может быть псевдопротоколом javascript:
После выполнения приведенного выше кода вы увидите знакомое окно предупреждения. Это то, о чем должны знать фронтенд-разработчики.
Перенаправление - распространенная функциональность, и при ее реализации необходимо учитывать эту проблему, чтобы избежать написания кода с уязвимостями.
Например, найденная уязвимость на сайте Matters News. Вот их страница входа:
После нажатия кнопки подтверждения вызывается функция redirectToTarget, код которой выглядит следующим образом:
После получения целевого URL, функция использует его напрямую для перенаправления: window.location.href = decodeURIComponent(target). А функция getTarget просто получает значение параметра target из строки запроса.
Таким образом, если URL входа - https://matters.news/login?target=javascript:alert(1), то при успешном входе у пользователя появится всплывающее окно, что является XSS-атакой!
Более того, XSS на странице входа имеет серьезные последствия. Она может напрямую захватить введенные значения, что означает кражу логина и пароля пользователя. Для проведения реальной атаки можно отправить фишинговые письма пользователям сайта с этой вредоносной ссылкой. Поскольку URL выглядит нормально, а целевая страница является настоящей, доверие к ней может быть высоким.
Кража логина и пароля через XSS после их ввода и входа, а также перенаправление пользователя на главную страницу без следов может привести к взлому учетной записи и ее захвату.
Подводя итог, можно сказать, что основная концепция XSS знакома многим. Однако вы можете быть не так хорошо знакомы с атаками с использованием псевдопротокола javascript:. Поэтому я хотел бы специально остановиться на этой проблеме, чтобы вы могли быть более осторожными и принимать соответствующие меры защиты при встрече с такими атрибутами.
Методы защиты
Вы можете обрабатывать URL самостоятельно, но давайте посмотрим, что обычно происходит, когда вы делаете это.
Поскольку вредоносная строка - javascript:alert(1), некоторые могут подумать, что достаточно проверить, начинается ли она с javascript: или убрать все вхождения javascript из строки.
Однако этот подход неэффективен, поскольку речь идет о содержимом атрибута href, а содержимое атрибута в HTML может быть закодировано. Другими словами, я могу сделать так:
Внутри нет содержимого, которое нам нужно фильтровать, и оно не начинается с javascript:, поэтому оно может обойти ограничение.
Лучший подход - разрешать только строки, начинающиеся с http:// или https://. Это обычно предотвращает любые проблемы. Некоторые более строгие методы включают использование JavaScript для разбора URL, например:
Таким образом, вы можете определить, является ли протокол допустимым на основе белого списка, и заблокировать любой контент, не находящийся в разрешенном списке.
Еще одна распространенная ошибка - использовать разбор URL на основе имени хоста или источника, например:
Когда hostname или host пусты, это означает, что URL недействителен. Хотя на первый взгляд этот метод может показаться приемлемым, мы можем использовать функцию JavaScript, где // рассматривается как комментарий, в сочетании с символами новой строки, чтобы создать строку, которая выглядит как URL, но на самом деле является псевдопротоколом javascript::
Хотя это похоже на URL, он отлично работает в Chrome без каких-либо проблем или ложных срабатываний. Однако Safari ведет себя по-другому. При выполнении того же кода в Safari 16.3 вывод:
Если вы действительно хотите использовать регулярное выражение для проверки, является ли это псевдопротоколом javascript:, вы можете обратиться к реализации в исходном коде React (многие библиотеки используют аналогичное регулярное выражение):
Из приведенного регулярного выражения можно увидеть гибкость javascript:. Он может иметь дополнительные символы перед началом и даже неограниченное количество символов новой строки и табуляции внутри строки. Вот почему я говорил, что самостоятельно обрабатывать его сложно, так как для понимания такого поведения нужно хорошо знать спецификацию.
Помимо упомянутых методов, простое добавление target="_blank" может иметь значительный эффект, поскольку многие браузеры уже устранили эту проблему.
В Chrome при нажатии на ссылку открывается новая вкладка с URL about:blank#blocked.
В Firefox открывается новая вкладка без URL.
В Safari ничего не происходит. Ни один из этих браузеров не выполняет JavaScript.
Тестируемые версии: Chrome 115, Firefox 116 и Safari 16.3.
В реальном мире большинство ссылок имеют атрибут target="_blank".
Однако если пользователь нажмет на ссылку средней кнопкой мыши вместо левой, ситуация может быть другой. Поэтому независимо от ситуации следует устранять первопричину, а не полагаться на защиту браузера.
Практический пример
В Telegram Web (у Telegram есть несколько веб-версий) есть функция ensureProtocol, которая проверяет наличие :// в URL. Если его нет, она автоматически добавляет http://:
Эту защиту легко обойти, используя что-то вроде javascript:alert('://'). Это позволяет успешно использовать псевдопротокол javascript:. Однако проблема в том, что сервер также проверяет, является ли URL действительным адресом, а предыдущая строка явно не является таковой.
URL может включать имя пользователя и пароль в начале (используется для HTTP-аутентификации), разделенные двоеточием (`:`), например:
Поэтому Slonser обнаружил, что эту строку можно использовать для обхода проверки:
Наконец, с помощью кодирования URL генерируется URL с паролем, который содержит только допустимые символы:
Сервер распознает указанную выше строку как ссылку, и клиент может обойти проверку ://. Когда пользователь нажимает на эту ссылку, запускается XSS-атака.
Заключение
В этой статье мы рассмотрели опасные аспекты псевдопротокола javascript:. Его можно поместить внутри атрибута href тега <a>, что является распространенным вариантом использования. Кроме того, разработчики часто забывают о потенциальных рисках или могут не знать о них, что приводит к уязвимостям. Хотя в большинстве случаев гиперссылки открываются на новых вкладках, предотвращая выполнение кода JavaScript, нет никакой гарантии, что поведение будет таким везде (например, когда целевой атрибут не указан) или при использовании старых браузеров или альтернативных методов для открытия новых вкладок. Это создает опасность для пользователей. Кроме того, при выполнении перенаправлений важно учитывать проблемы, связанные с псевдопротоколом javascript:. Без надлежащей обработки данных - это может привести к XSS-уязвимости. Разработчикам крайне важно постоянно знать об этих проблемах и соответствующим образом решать их в коде, как сказано в знаменитой цитате:
Никогда не доверяйте пользовательскому вводу.
Last updated