Контакты
Подписка
МЕНЮ
Контакты
Подписка

Дыра в SOP: баг или фича?

Дыра в SOP: баг или фича?

В рубрику "Оборудование и технологии" | К списку рубрик  |  К списку авторов  |  К списку публикаций

Дыра в SOP: баг или фича?

Same Origin Policy (SOP), или по-русски принцип одного источника, – один из столпов безопасности современных браузеров. Многие клиентские приложения действуют от имени сторонних ресурсов. Например, браузеры “ходят” по HTTP-редиректам – инструкциям от сервера, браузеры же предоставляют Document Object Model (DOM) интерфейсы для скриптов.
Илья Сафронов
Пентестер, Practical Security Lab, itpsl.ru

Если бы не существовало никакой модели безопасности, приложения могли бы выполнять зловредные действия для юзера или Web-ресурсов. Со временем многие Web-технологии пришли к единой модели безопасности, известной нам как Same-Origin Policy.

По сути, SOP запрещает сторонним скриптам обращаться к текущей вкладке в браузере путем проверки источника – Origin. Источник определяется комбинацией протокола, URL и порта:

http:// + www.example.com + :80

Любой скрипт, пришедший с другого Origin, не имеет права доступа к содержимому документа и его cookies. Зачем это нужно? Представим себе ситуацию, когда скрипт из любой вкладки браузера имеет доступ к другим вкладкам. В такой ситуации, один раз посетив любой нехороший сайт, мы отдадим все свои cookies, а также все содержимое наших вкладок – переписки, картинки и т.д. Чтобы такого не происходило, все современные браузеры используют SOP1.

Шло время, технологии развивались, и у разработчиков все чаще возникала необходимость все-таки обращаться к другим Origins. Простейший пример – когда сайт использует api, а api находится на другом домене и, соответственно, другом источнике. По умолчанию браузер не разрешит такой запрос. И тут на помощь приходит CORS2.

С помощью CORS (Cross-Origin Resource Sharing) ресурс может указать, какие сторонние источники (Origins) могут получить доступ к его содержимому. Оковы пали! Наконец-то разработчики могут пересылать всякое интересное между разными ресурсами.

Что может пойти не так?

В стандарте CORS описывается довольно много заголовков для разных целей, наиболее интересными для нас выглядят эти три:

Access-Control-Allow-Origin

Указывает, какие домены имеют доступ к ресурсам текущего домена. То есть если домен нужен-доступ.рф хочет иметь доступ к домену можем-дать-доступ.рф, разработчики последнего могут использовать этот заголовок.

Access-Control-Allow-Credentials

Указывает, будет ли браузер посылать cookie вместе с запросом. Куки будут отсылаться только если значение заголовка равно True.

Access-Control-Allow-Methods

Указывает, какие HTTP-методы (GET, PUT, DELETE и т.д.) могут быть использованы для доступа к ресурсу.

Практика

Представим, что мы отправили вот такой запрос к сайту:

GET /api/userinfo
Host: vlopate.com
Origin: vlopate.com

И в ответ получаем, среди прочего, следующее:

HTTP/1.0 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Что это значит?

Это значит, что vlopate.com отдает информацию о пользователе любому сайту. Все, что осталось, – это создать зловредный ресурс, разместить на нем код, обращающийся к vlopate.com/api/user-info, и заманить на этот ресурс пользователей. Все их данные принадлежат нам.

Допустим, разработчики все-таки задумались о безопасности и настроили CORS, на запрос:

GET /api/userinfo
Host: vlopate.com
Origin: vlopate.com

Мы получаем:

HTTP/1.0 200 OK
Access-Control-Allow-Origin: vlopate.com
Access-Control-Allow-Credentials: true

И все, казалось бы, хорошо, но разработчик был с похмелья и в настройках бэкенда указал проверку следующим образом:

if ($_SERVER['HTTP_HOST'] == '*vlopate.com'):
access.granted()
else:
raise.404()

Видите звездочку (*)? Это означает, что мы можем зарегистрировать домен mi_ne_vlopate.com, и на наш запрос:

GET /api/userinfo
Host: vlopate.com
Origin: mi_ne_vlopate.com

получим утвердительный ответ:

HTTP/1.0 200 OK
Access-Control-Allow-Origin: mi_ne_vlopate.com
Access-Control-Allow-Credentials: true

Через неделю разработчики отошли от празднования релиза и поправили код на:

if ($_SERVER['HTTP_HOST'] == '*.vlopate.com'):
access.granted()
else:
raise.404()

тем самым разрешив доступ только поддоменам.

Придется идти на крайние меры. Поиск дает результат – найдена XSS на поддомене kartofel.vlopate.com. А это значит, при ее эксплуатации мы все равно получим доступ к данным vlopate.com, так как наш скрипт работает от имени его поддомена.

Какие выводы тут можно сделать?

При разработке внимательно относитесь к настройке CORS, также внимательно относитесь к нему при аудитах или багбаунти.

Для проверки могут помочь готовые скрипты из github: https://github.com/chenjj/CORScanner   https://github.com/RUB-NDS/CORStest

Отмечу, что всецело на них полагаться не стоит, важные и интересные случаи лучше дополнительно проверить руками.

Успехов!

___________________________________________
1 https://tools.ietf.org/html/rfc6454
2 http://www.w3.org/TR/cors/

Опубликовано: Журнал "Information Security/ Информационная безопасность" #4, 2019

Приобрести этот номер или подписаться

Статьи про теме