XSS и Same Origin Policy

Владимир | | Ajax, HTML, JavaScript, Web разработка.

xss same origin policy

Прошлый пост «Как обойти запрет на XSS» вызвал довольно интересное обсуждение. Я сначала пробовал сразу отвечать на комментарии, но потом понял, что развернутые ответы больше напоминают статьи, а краткие – только всё запутывают.

И, что хуже всего, я почувствовал, что не могу внятно ответить на часть комментариев, а значит нужно подробнее разобраться в ситуации 🙂

В общем, я провел небольшую серию экспериментов и хочу рассказать о результатах.

На всяких случай напомню, что в прошлый раз речь шла об использовании XSS (cross site scripting), т.е. межсайтовых запросах.

В первую очередь я решил проверить, как работают самые простые запросы. Для этого создал простенькую тестовую страничку на локальном сервере (localhost) и подключил библиотеку jQuery.

1) Метод load.

$().load("http://api.wipmania.com/wip.js", {}, function() {
	alert('OK!');
});

Этот скрипт пытается получить содержимое файла wip.js с сервера api.wipmania.com.

В результате получаем ошибку Access to restricted URI denied

access_restricted_load

Как видите, FireFox заблокировал этот запрос.

2) Попробовал использовать getJSON – та же самая ошибка

$.getJSON("http://api.wipmania.com/wip.js", {}, function() {
	alert('OK!');
});

На данном этапе всё работает именно так, как и должно. Браузер придерживается Same Origin Policy, т.е. политики безопасности, которая запрещает с помощью JavaScript получать данные из источника отличного от того, с которого был загружен сам скрипт.

В данном случае это означает, что раз скрипт загружен с localhost, то получить данные с api.wipmania.com он не может.

Продолжаю эксперименты.

3) В этот я раз решил посмотреть, как работает метод getScript. Дело в том, что Big_Shark привел пример рабочего скрипта, который получает данные с помощью XSS.

$.getScript("http://api.wipmania.com/wip.js",
	function() {
		alert(WIPlocation.address.country);
});

Тут всё гораздо интереснее. Приведенный скрипт отлично работает и на первый взгляд складывается впечатление, что Same Origin Policy не применяется.

Но это не так. Просто метод getScript выполняет несколько дополнительных операций. В этом легко убедиться, если с помощью отладчика (я использовал Firebug) остановить выполнение скрипта на 3-ей строке.

Если в этот момент переключиться на вкладку HTML, то увидим такую картину.

getscript

Внутри заголовка страницы появился новый тег script с данными от wipmania. Обратите внимание, на его атрибут src. Т.е. получается, что из этого скрипта можно отправлять запросы на сервер api.wipmania.com и при этом будет соблюдаться Same Origin Policy.

Примечание. В данном примере отправлять запросы из этого скрипта нет смысла, т.к. он сам по себе содержит все необходимые данные.

После завершения работы функции jQuery этот тег удаляет.

4) В последнем эксперименте я решил использовать JSONP.

Подробно о его результатах и вообще об этом формате я напишу в одном из следующих постов. Сейчас только расскажу о тех моментах, которые касаются XSS.

В принципе, ситуация аналогична предыдущему примеру. jQuery использует дополнительный тег script с помощью которого и обеспечивается доступ к другому домену.

Кстати, формат очень удобный, особенно учитывая то, что он поддерживается многими JavaScript библиотеками.

Вам нужно только отправить запрос и написать обработчик (функцию), который получит ответ сервера, а всё остальное библиотека сделает за вас. Создаст тег script с соответствующим атрибутом src, отследит момент получения данных, вызовет ваш обработчик и удалит script.

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

1) С помощью прокси (метод описан в статье «Как обойти запрет на XSS» (http://www.simplecoding.org/kak-obojti-zapret-na-xss.html)).

2) С помощью Flash. Сам я не пробовал, но надежные источники утверждают, что этот так. По идее должно работать и для Silverlight.

3) С помощью тега script. Если вам нужно обращаться к какому-нибудь серверу, то придется добавить скрипт с этого сервера на свою страницу. Т.е. всё будет работать в рамках Same Origin Policy. Кстати, именно так работают Google Maps и Яндекс.Карты.

Может быть, существуют и другие варианты. Если есть идеи – пишите в комментариях, обсудим 😉

  • Big_Shark

    Спасибо)
    Полезная статья получилась.
    Только я не понимаю почему JSONP а не JSON вы пишите?
    Насколько я понял 2 эксперимент наработал именно из за localhost?
    Можно еще рассмотреть вариант загрузки google.load но я подозреваю что он работает также как и getScript в jQuery.
    Очень жду статью про JSON так как использую его повсеместно и считаю его пока лучшим стандартом для передачи данных.
    Авось что не-будь новое узнаю.
    P.S. Нужна будет помощь с JSON в любом виде пишите буду помогать.

    • Спасибо, статью я практически дописал. А помощь в виде полезных замечаний и предложений всегда приветствуется 😉

  • Big_Shark

    Спасибо)
    Полезная статья получилась.
    Только я не понимаю почему JSONP а не JSON вы пишите?
    Насколько я понял 2 эксперимент наработал именно из за localhost?
    Можно еще рассмотреть вариант загрузки google.load но я подозреваю что он работает также как и getScript в jQuery.
    Очень жду статью про JSON так как использую его повсеместно и считаю его пока лучшим стандартом для передачи данных.
    Авось что не-будь новое узнаю.
    P.S. Нужна будет помощь с JSON в любом виде пишите буду помогать.

    • Спасибо, статью я практически дописал. А помощь в виде полезных замечаний и предложений всегда приветствуется 😉

  • Big_Shark

    Все понял почему JSONP а не JSON можете не отвечать.

  • Big_Shark

    Все понял почему JSONP а не JSON можете не отвечать.

  • Frenzy

    в список «костылей» тогда и Java-апплеты можно добавить рядом с флэшем и сильверлайтом 🙂 но такие методы конечно конкуренции не выдерживают

    а вобще светлое будущее наступит уже скоро — реализация кроссдоменных запросов уже давно существует в форме в3ц-драфта, а в скором времени материализуется и в браузерах (фаерфокс 3.1, ие8, в вебките уже есть).

    • По-моему, у Java-апплетов есть своя «песочница» с довольно жесткими ограничениями.

      Flash и Silverlight не будут «костылями» если они используются не только для отправки запросов.
      Хотя, конечно, вы правы, использовать эти технологии (закрытые, кстати) для получения данных — это перебор.

  • Frenzy

    в список «костылей» тогда и Java-апплеты можно добавить рядом с флэшем и сильверлайтом 🙂 но такие методы конечно конкуренции не выдерживают

    а вобще светлое будущее наступит уже скоро — реализация кроссдоменных запросов уже давно существует в форме в3ц-драфта, а в скором времени материализуется и в браузерах (фаерфокс 3.1, ие8, в вебките уже есть).

    • По-моему, у Java-апплетов есть своя «песочница» с довольно жесткими ограничениями.

      Flash и Silverlight не будут «костылями» если они используются не только для отправки запросов.
      Хотя, конечно, вы правы, использовать эти технологии (закрытые, кстати) для получения данных — это перебор.

  • по моему не очень законно обходить чужие запреты.

    • А где тут чужие запреты? Используются только обычные технологии, без них бы многие сервисы не работали. Например, карту Google или Яндекса вы бы на свой сайт не добавили.

  • по моему не очень законно обходить чужие запреты.

    • А где тут чужие запреты? Используются только обычные технологии, без них бы многие сервисы не работали. Например, карту Google или Яндекса вы бы на свой сайт не добавили.

  • Спасибо !
    Интересно.

  • Хочу добавить, что с портом отличным от 80, по 
    Same origin policy межсайтовые запросы будут запрещены, даже если вы подключите скрипт с другого сайта вручную на своей странице.

  • Подключаю скрипт со стороннего хоста по IP:

    По идее должны работать запросы из него к тому же хосту http://xx.xx.xx.xx?

    Да вот что-то не работают. POST в Firebug'е заменяется на OPTIONS, при этом даже вкладки Post в этом запросе нету и ответ пустой.

    Почему еще запросы могут не пускать? IP внешний. Или может такое только для доменов разрешено?

    • Посмотрите вкладку net. Firebug выводит ошибку в случе кроссдоменного запроса. Только в вашем случае скрипт должен подключиться, это не кроссдоменный запрос. Точно таким же способом подключаются, напрример, google maps.

      • В том то и дело что должен, но почему-то не подключается =

        Однако я использую jQuery, поэтому просто реализовал это через JSONP, там поддерживается такой формат. Теперь все работает.

  • Vasyl Khrystyuk

    >> По идее должно работать и для Silverlight.

    Авотфиг.
    Формат майкрософтовского crossdomain.xml настолько отличается от принятых в сети гуглом и адобом форматов, что ничего не позволяет взять с другого домена.
    Поэтому там берут  данные как ресурс (т.е. похожим методом на src) но тогда ограничение на использование его такие же как и в яваскриптаю Или же делают прокси на сервере.

    При чем последний вариант — единственный выход в многих случаях.