Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)
XSLeaks, сокращенно от Cross-Site Leaks, относится к технике использования определенных приемов для раскрытия информации с других веб-сайтов. Хотя по определению эта тема должна быть размещена в главе о "Cross-Site Attacks", я решил поместить ее в конце, чтобы придать ей большее значение.
Это самая интересная и любимая тема для меня при изучении безопасности фронтенда. Если бы в университете существовал "Отдел безопасности фронтенда", XSLeaks, вероятно, был бы элективным курсом на третьем или четвертом курсе. Это означает, что вам нужно иметь много предварительных знаний, прежде чем браться за эту тему. Она включает в себя взаимодействие между фронтендом и бэкендом, операции браузера, различные фронтенд-техники, а также воображение и креативность. Вот почему я нахожу это увлекательным.
Чтобы понять, что такое XSLeaks, нам нужно начать с того, что такое Side-Channel Attacks Are.
Side-Channel Attacks 101
Атаки через побочные каналы упоминались при обсуждении уязвимостей CPU, таких как Meltdown и Spectre.
Один из моих любимых примеров атак через побочные каналы — классическая "проблема с лампочками" (хотя она появилась в "Alice in Borderland", я помню, что она существовала даже раньше).
Представьте, что у вас есть три выключателя в вашей комнате, каждый из которых соответствует лампочке в другой комнате. Эти две комнаты разделены дверью, поэтому вы не можете видеть другую комнату. Вы можете свободно управлять выключателями, и у вас есть только один шанс войти в другую комнату и вернуться.
После возвращения вам нужно ответить, какой выключатель соответствует каждой лампочке. Как бы вы это сделали?
Если бы было только две лампочки и два выключателя, это было бы просто. Допустим, они обозначены A и B. Вы бы включили выключатель A, пошли в другую комнату, и лампочка, которая горит, соответствует выключателю A, в то время как та, которая не горит, соответствует выключателю B.
Но что, если есть три лампочки? Что вам делать?
Ответ на эту классическую проблему заключается в том, чтобы включить выключатель A на несколько минут, затем выключить его и включить выключатель B. Теперь вы можете пойти в соседнюю комнату. Лампочка, которая горит, соответствует выключателю B. Но как определить другие две лампочки?
Вы можете прикоснуться к лампочкам рукой. Та, которая теплая, представляет собой лампочку, которая была включена недавно, поэтому она соответствует выключателю A, в то время как та, которая холодная, соответствует выключателю C.
В этой задаче, помимо яркости, мы можем также сделать вывод о том, была ли лампочка включена или выключена, основываясь на побочном эффекте, который она производит при включении: температуре. Это и называется атакой через побочные каналы.
Другой пример часто встречается в детективных фильмах, когда вы касаетесь капота машины на парковке. Если он теплый, это означает, что машина была припаркована недавно. Это также форма атаки через побочные каналы.
Когда этот принцип применяется к разработке фронтенда, это называется XSLeaks.
Как я уже подчеркивал, для браузеров важно предотвратить доступ веб-сайта к информации другого веб-сайта. Это называется Same Origin Policy, и браузеры внедрили множество ограничений, таких как отображение сообщений об ошибках при доступе к другим веб-сайтам, которые нарушают Same-Origin Policy.
XSLeaks пытается обойти эти ограничения в разработке фронтенда, используя техники атак через побочные каналы для раскрытия информации другого веб-сайта.
Как обычно, давайте рассмотрим пример.
Experiencing XSLeaks in Action
Эта веб-страница используется для определения, на каких веб-сайтах вы вошли в систему. Для меня результат выглядит следующим образом:
Как это работает?
Во-первых, при загрузке изображения вы можете использовать атрибуты onerror и onload, чтобы определить, было ли изображение загружено успешно, как показано ниже:
Определение "успешно загружено" включает не только то, что код статуса ответа равен 200, но и то, что содержимое действительно является изображением. Если вместо этого загружается веб-страница, событие onerror все равно будет вызвано.
Сочетая загрузку изображения с перенаправлением после входа в систему, можно определить, вошел ли пользователь на Cross-Origin веб-странице.
В данном URL, если пользователь не вошел в систему, он будет перенаправлен на страницу входа. Если пользователь вошел в систему, он будет перенаправлен на логотип Medium. Поэтому HTML можно записать следующим образом:
Если пользователь вошел в систему, он будет перенаправлен на URL логотипа сайта, и поскольку это изображение, событие onload будет вызвано. С другой стороны, если пользователь не вошел в систему, он будет перенаправлен на страницу входа, которая не является изображением, поэтому событие onerror будет вызвано.
Таким образом, мы можем использовать это поведение "перенаправления после входа", в сочетании с тем, загружено ли изображение или нет, чтобы определить, вошел ли пользователь в систему. Это классический пример XSLeaks.
Определение того, вошел ли пользователь в систему, может быть не очень полезным, поэтому давайте рассмотрим более практический пример.
XSLeaks using Status Codes
При загрузке содержимого с помощью <img>
помимо проверки кода статуса также проверяется, является ли ответ изображением. Поэтому его можно использовать только для определения, является ли "последнее загруженное содержимое изображением". С другой стороны, <script>
ведет себя иначе. Если код статуса ответа равен 200, даже если содержимое не является JavaScript, событие onerror
не будет вызвано.
Для <script>
, если код статуса равен 200, это означает, что содержимое URL было успешно загружено, поэтому будет вызвано событие onload
. Однако, если JavaScript-код внутри недействителен, будет показана ошибка.
Таким образом, мы можем косвенно определить, успешен ли код статуса URL, используя тег <script>
, следующим образом:
Результат будет либо 200_load
, либо 400_error
, но сообщение об ошибке все равно будет отображено в консоли:
Uncaught SyntaxError: Unexpected token '<' (at 200:1:1)
Итак, что мы можем сделать зная код статуса ответа? Давайте рассмотрим реальный пример.
Если я не вошел в систему или если я вошел в систему, но USER_ID не совпадает, будет возвращен код статуса 403 вместе с сообщением об ошибке:
{"error":{"message":"You are not logged in as a user that has access to this developer.twitter.com resource.","sent":"2019-03-06T01:20:56+00:00","transactionId":"00d08f800009d7be"}}.
Если я вошел в систему и USER_ID правильный, будет возвращена информация о пользователе. Этот дизайн вполне приемлем для контроля доступа, поскольку пользователи не могут получить доступ к данным других людей. Однако различие в кодах ответа создает возможность для XSLeaks.
Эксплуатация работает следующим образом: предположим, я знаю USER_ID в Twitter, скажем, 12345. Я могу написать следующий код на своем блоге:
Это уязвимость, нарушающая конфиденциальность. Когда вы посещаете веб-сайт, на котором вы еще не были, он может точно определить, "являетесь ли вы определенным человеком", используя этот метод, что довольно опасно.
Итак, как можно исправить эту уязвимость?
One of the Defense Mechanisms against XSLeaks
Самый простой механизм защиты — это куки. Установив куки на SameSite=Lax
, независимо от того, используется ли <img>
или <script>
, куки не будут отправлены, что позволит избежать упомянутых ранее проблем.
В настоящее время браузеры включают этот механизм по умолчанию, поэтому даже если разработчики допускают ошибки, они все равно будут защищены, если только не установят куки на SameSite=None
. На самом деле, есть веб-сайты, которые это сделали. Веб-сайт, который мы изначально посетили, чтобы определить, вошел ли пользователь в систему, может обнаружить только веб-сайты, у которых включен SameSite=None
.
Кроме кук с тем же Site, есть несколько других способов защититься от таких атак.
Первый — это заголовок Cross-Origin-Resource-Policy, упомянутый ранее при обсуждении CORS. Этот заголовок, может предотвратить загрузку этих ресурсов с других веб-сайтов.
Если вы добавите Cross-Origin-Resource-Policy: same-origin, в предыдущем примере, независимо от того, 200 это или 400, скрипт выполнит событие onerror, потому что оба будут заблокированы CORP. Консоль покажет следующие ошибки:
GET http://localhost:5555/200 net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin 200 (OK)
GET http://localhost:5555/400 net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin 400 (Bad Request)
Второй метод — это новый механизм, называемый Fetch Metadata
. Когда веб-страница отправляет запрос, браузер автоматически добавляет заголовки со следующей информацией:
Sec-Fetch-Site
: The relationship between the requesting site and the target site.Sec-Fetch-Mode
: The mode of the request.Sec-Fetch-Dest
: The destination of the request.
Сервер может использовать эти заголовки для принятия превентивных мер. Например, если сервер ожидает только API-вызовы и не ожидает загрузки ресурсов с помощью <script>
или других тегов, он может заблокировать такое поведение:
Возможные значения для Sec-Fetch-Site
:
Same-Origin
Same-Site
Cross-Site
None (for cases like when the browser opens a website from a bookmark)
Возможные значения для Sec-Fetch-Mode
:
Same-Origin
No-Cors
Cors
Navigate
Существует слишком много возможных значений для Sec-Fetch-Dest
, поэтому я не буду их перечислять.
Третий метод заключается в изменении как успешных, так и неудачных кодов состояния на 200, что делает невозможным обнаружение различий на основе кодов состояния.
Это напоминает мне о повторяющейся проблеме на форумах обсуждения бэкенда: как устанавливать код состояния ответа. Например, некоторые люди рассматривают код состояния как статус самого ресурса. Например, если /api/books/3 не существует, они возвращают 404 Not Found.
Однако некоторые люди используют код состояния для другой цели. Хотя /api/books/3 не имеет конкретной книги, API сам по себе существует, поэтому они возвращают 200 и включают сообщение "не найдено" в тело ответа. Только при доступе к несуществующему API, такому как /api/not_exist, они возвращают 404.
С этой точки зрения, второй подход проектирования может решить проблему XSLeaks. Однако, на мой взгляд, не стоит изменять коды состояния специально для защиты от атак. Это связано со многими зависимостями и может потребовать изменений на фронтенде. Лучший подход — сначала использовать Cookies Same-Site
для защиты, так как это самое простое и легкое решение.
Other potential leaks
В HTML есть несколько вещей, которые могут служить утечками или маркерами утечек. Один из примеров — количество фреймов.
Ранее упоминалось, что браузеры ограничивают доступ к окну с другого источника, ограничивая информацию, к которой можно получить доступ. Например, хотя вы можете использовать location = '...'
для перенаправления, вы не можете получить доступ к location.href
или другим значениям.
Тем не менее, даже при этих ограничениях все еще можно получить некоторую информацию, такую как количество фреймов. Вот пример кода:
Если на открытой странице есть iframe
, длина будет равна 1. Если ничего нет, длина будет равна 0. Если у веб-сайта разное количество iframes
в зависимости от различных действий, мы можем использовать эту технику для их обнаружения.
Facebook имеет функцию поиска, которая позволяет пользователям искать друзей, посты, фотографии и т. д. Эта функция поиска предназначена для легкого обмена, поэтому пользователи могут получить к ней доступ напрямую через URL. Например, URL https://www.facebook.com/search/str/chen/users-named/me/friends/intersect
отобразит результаты поиска для друзей с именем "chen".
Автор обнаружил различие в поведении: если в результатах поиска есть что-то, на странице будет iframe
, и автор предполагает, что это может быть целью отслеживания Facebook. Если результатов нет, то iframe
не будет.
Другими словами, мы можем определить, есть ли результаты поиска, проверяя frames.length
.
Процесс атаки выглядит следующим образом: мы сначала подготавливаем HTML с следующим содержимым:
Затем мы отправляем эту веб-страницу нашей цели. После того как цель откроет веб-страницу, сервер атакующего получит результаты поиска.
Защита от такого типа атаки более сложна, потому что cookie с атрибутом Same-Site Lax
здесь не помогут. Код использует window.open
, и если вы не установите его в строгий режим, cookie будут отправлены вместе с запросом.
Ранее упомянутая Fetch Metadata
также неэффективна, потому что это на самом деле обычный запрос.
Если вы хотите защититься от этого, используя существующие механизмы, вы можете добавить заголовок COOP (Cross-Origin-Opener-Policy). Таким образом, открытое окно потеряет связь с оригинальным окном, и не сможет получить доступ к win.frames
.
Другой вариант — изменить страницу результатов поиска. Независимо от того, есть ли результаты поиска или нет, либо всегда иметь iframe
, либо никогда его не иметь, чтобы информация не могла быть утечкой через количество iframes
.
Заключение
В этой статье мы узнали, что такое атаки через побочные каналы, и поняли основные принципы XSLeaks. Мы также увидели несколько реальных примеров, которые демонстрируют, что это действительно уязвимость, которую можно эксплуатировать.
Конечно, XSLeaks обычно требует больше предварительных условий и условностей по сравнению с другими уязвимостями, и результаты, которые можно получить, ограничены. Тем не менее, я лично считаю, что это все еще очень интересная уязвимость.
Google сам имеет специальную страницу, посвященную XSLeaks в своей программе вознаграждений за ошибки, потому что они уже осведомлены о большинстве проблем и имеют инженеров, которые специально исследуют эту область. Поэтому не рекомендуют багхантерам тратить свое время на это.
Ссылки:
Last updated