5 октября 2008 в 19:30

JavaScript Cross Site (XSS) POST

Недавно, в Dojo появилась возможность производить cross site POST запросы, т.е. отправка POST запросов на другие сайты, с другими доменными именами. Это событие осталось незамеченным в нашем сообществе JavaScript разработчиков. По крайней мере, никто и слова про это не сказал. А зря…

В один прекрасный момент понадобилась возможность осуществлять cross-site POST запросы. Естественно понимая, что на данный момент безопасность браузеров запрещает cross-site AJAX, был выбран вариант использования Flash-AJAX связки. Но все, же лично мне нравятся «чистые» JavaScript решения. Поэтому до конца мы все же не удовлетворились. Недавно утром пребывая в альфа состоянии сна, мне приснился принцип на основе которого, можно осуществлять XSS POST используя только JavaScript. Суть реализации XSS POST основана на особенностях хранения данных в свойстве window.name. Мысли пришли благодаря недавним сериям статей в Инете про window.name. Покопавшись в Google, сразу же нашлась уже готовая реализация. (Видать приснилось не только мне). Dojo как всегда впереди планеты всей www.sitepen.com/blog/2008/07/22/windowname-transport. Молодцы.


Как это работает


Принцип работы довольно подробно описан по ранее указанной ссылке. В двух словах: если изменить свойство window.name оно остается измененным пока браузер (вкладка-табулятор) открыт. Если изменить свойство на одном сайте, то его можно потом прочитать на другом. Т.е. при переходе от сайта к сайту браузер не очищает window.name. Причем данный механизм хранения значения window.name работает во всех браузерах. XSS POST транспорт, использует эту замечательную возможность. Другими словами: при отправке данных на другой сервер происходит следующее:
  1. загружаем страницу с иного сайта/домена
  2. эта страница оставляет свой след в window.name
  3. возвращаемся на свой сайт/домен
  4. считываем оставленный след из window.name

В добавок, для передачи POST запроса используется трюк c атрибутом target элемента form (принцип AJAX Uploader, Gmail Uploader файлов).
Ответ от сайта на который идет запрос должен быть в следующем формате:
<script>
window.name = ‘тут пишем то, что хотим передать’;
</script>

Соответственно данный подход не позволяет забрать данные без ведома владельца иного сайта, потому что ответ должен быть сформирован в определенном специфическом формате. Это обстоятельство сохраняет безопасность и защищает от XSS скрабинга. Т.е. по сути это чем-то похоже на механизм разрешения кросс-доменных запросов (cross-domain.xml) во Flash.


Более детально


  1. Создается iframe и вешается обработчик на событие onload.
  2. Если необходим просто GET запрос, то у iframe в атрибуте src устанавливается URL адрес ресурса. Если необходим POST запрос то динамически создается форма. Адрес ресурса уже указывается в атрибуте action объекта формы, а параметры передаются в элементах input (name=имя параметра, value=значение параметра). Чтобы ассоциировать iframe с формой (form) у нее ставится такой же идентификатор (атрибут id) что и у iframe. После генерации формы просто вызывается метод формы submit. Запрос произведен.
  3. Функция обратного вызова указанная в iframe срабатывает после того как данные загрузились. В нашем случаем загружается конструкция вида <script>window.name=’данные’</script> и данные попадают в свойство name объекта contentWindow. Задача обработчика достать эти данные. И если вы произведете обращение к iframe.contentWindow.name то будет отказано в доступе. Чтобы обойти эту проблему нужно вернуться в свой домен, делается это сменой значения аттриубута location объекта contentWindow. Например, можно указать адрес на пустой blank.html со своего домена или просто записать «about:blank» (iframe.contentWindow.location = ‘about:blank’). После этого можно считать данные из атрибута name.


Относительно текущей реализации от Dojo


Проанализировав их разработку
dojox.io.windowName.js мы нашли ряд значительных недостатков:
  1. Сильная зависимость от ядра, которое весит довольно «некисло».
  2. Необходимость наличия «resources/blank.html» файла.

В добавок:
  1. нам не понравился код, наше субъективное мнение – грязный код.
  2. для FireFox применяется дополнительные «телодвижения» (комментарий в исходниках относительно этого: FF2 allows unsafe sibling frame modification) – честно говоря мы так и не поняли для чего именно…

Нам захотелось улучшить текущее состояние XSS POST и, соответственно, написали свою реализацию, которая гораздо меньше по объему, самодостаточная и не привязана ни к одной из библиотек, код более чистый и оптимальный. Сравнивайте и судите сами:

Пример вызова:
SRAX.XSS.post('http://okarta.ru/scripts/post-xhr.php', 'a=a1&b=b2', function(text){
  alert(text)
})




XSS POST в нашей реализации в дальнейшем будет дорабатываться и развиваться, соответственно обрастет большим количеством методов и параметров. Кому интересно, сможет всегда скачать более новую версию.
Как всегда существует общий недостаток: при использовании iframe для передачи данных в браузере появляется история, т.е. активируется кнопка «Назад». Может кто знает как вставить iframe без срабатывания истории? Было замечательно устранить этот недостаток.


Зачем это надо


Давно существуют механизмы XSS GET запроса данных. И по сей день зачастую их бывает достаточно для решения большего количества задач. Но все же, иногда бывают моменты, когда ограничения GET запросов не позволяют качественно решить нужную задачу. К примеру, Google AJAX Language API реализовано на XSS GET запросах и имеет ограничение на количество переводимых символов за один запрос – 500 символов. Теперь имея XSS POST данное ограничение можно смело убрать. Есть и другие случаи, когда XSS POST жизненно необходим. Грамотным специалистам XSS POST транспорт даже очень пригодится.

Автор статьи – Руслан Синицкий, соавтор – Александров К. М. (m007)
sirus @sirus
карма
29,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (73)

  • –3
    Вроде jQuery давненько умеет такое, достаточно добавить в конец запроса "?" и все сработает.
    Только не понимаю такой реакции на это.
    • +2
      ну может и неглубоко :)… существуею плагин для jQuery plugins.jquery.com/files/jQuery.windowName.plugin.js.txt, тоже недавно сделали…
    • +1
      В статье речь идет о кроссдоменных POST запросах. Кроссдоменный GET может в jQuery и есть (и скорее всего реализуется через элемент script).
    • +1
      Скорее всего вы думаете про JSONP.
  • +3
    вы глубоко заблуждаетесь
    • +1
      промахнулся кнопкой, этот комментарий относительно первого комментария akira
  • +2
    у iframe style="display:none" дважды идет…

    а так — молодцы, что еще сказать :)
    • 0
      да, точно, механическая ошибка :)… спасибо.
  • 0
    в избранное! Спасибо, будет полезно :)
  • +6
    СРАКС — клево звучит!
    • +2
      а че минусуте человека :), согласен — звучит клево! да и название запоминающееся :). на самом деле это аббревиатура.
  • 0
    • +1
      > Суть реализации XSS POST основана на особенностях хранения данных в свойстве window.name.
      • 0
        мм? sessvars основаны на этом же самом факте.
    • 0
      и что? непонятно как указанная вами статья коррелируется с текущей. вы бы хоть чтото словами добавили, а то непонятно зачем тут эта ссылка
      • 0
        Просто похожая статья, похожий метод, похожие идеи. Так, к размышлению.
        • 0
          ну так бы и написали :). да, ваша статья тоже очень интересна и я пользуюсь таким подходом в своих приложениях, и другим советую.
    • –1
      Собственно, да — об этом писали ещё в мае.
      • 0
        О кроссдоменном POST?
        • 0
          О ключевом механизме этой кроссдоменности.
          • 0
            Механизм то ключевой, но о том что можно сделать альтернативу XMLHttpRequest где будет реализация HTTP методов GET и POST в той статье даже и намека не было.
            • 0
              Да я не против — познавательно, по-человечески написано.
              Просто подтвердил для тех, кто в шоке, что про window.name уже крики были.
  • 0
    а у меня почемуто не получилось считать window.name из одного окна, установив его в другом
    • 0
      точнее нигде кроме експлорера не видно. версии последние
    • +3
      Да и аватар у вас не грузится… )
      • 0
        блин. только заметил. да шо ж это такое? не грузится ничего… а если серьезно где я не прав?
        • 0
          window.name распространяется на одну и туже вкладку в одном и том же окне
  • 0
    Спасибо за идею и за скрипт.

    С Вашей подачи разбираюсь с Google AJAX Language API :)
  • 0
    А не лучше ли кросс-доменные AJAX запросы делать через «прокси»-скрипт на сервере? Это к тому же позволяет контролировать в одном месте все проходящие запросы (например, в целях предотвращения уязвимостей).
    • 0
      не всегда, есть случаи когда XSS POST жизненно необходим. Судить лучше или хуже надо в конкретной ситуации относительно конкретно решаемой задачи.
      К примеру возьмите тотже Google AJAX Language API — вам было бы удобно ставить к себе прокси?
      • 0
        Ну не знаю как Гугл АПИ, не пользовался, но прокси-скрипт имеет и свои преимущества, все таки создавать ифреймы, сабмитить туда формы — очень извратно… что-ли. По моему, это явно неправильный способ, и кросс-доменные запросы не зря ограничиваются браузером.
  • 0
    не стоит сильно полагаться на то, что .name сохраняется при переходе между доменами — вряд ли это поведение описано в каком-нибудь стандарте, и уже сейчас вряд ли оно будет работать в хроме (его разработчики вроде обещали полностью очищать все данные при смене домена)

    а насколько я понял по описанию принцип работы плагина dojo, там используется старый *проверенный* способ кроссдоменной передачи данных. Кстати, имхо именно по причине древности «это событие прошло незамеченным»
    • 0
      1) >принцип работы плагина dojo, там используется старый *проверенный* способ кроссдоменной передачи данных

      принцип работы dojo такой же как и у нас. мы используя тот же подход сделали другим образом (другая реализация).
      2) >не стоит сильно полагаться на то, что .name сохраняется при переходе между доменами
      сохраняется 100%

      3) >вряд ли оно будет работать в хроме
      почему не проверили? работает 100%

      4) > Кстати, имхо именно по причине древности «это событие прошло незамеченным»
      кстати как раз этого и достаточно чтобы «дожить» до нормальной реализации XDomain, которая заложена в новых стандартах W3ORG
    • 0
      Нарушения безопасности нет. Потомучто довереный домен должен возвращать данные в определенном формате, поэтому это закрывать не будут, а если и будут то уже в спецификации HTML5 предусмотрены функции для кроссдоменного доступа и это работает уже в FF3. Остальные браузеры поднятнутся. Зачем это нужно? Покажут новые проекты.
      • 0
        гыгыгы, «определённый формат» % )
        security through obscurity, да? Если да, то не позорьтесь: )
        • 0
          Вы бы термины не применяли в которых не разбираетесь ;)

          Формат ответа window.name=’данные’ не для того чтобы запутать злоумышленника, а для того чтобы наоборот открыть данные для кроссдоменных запросов. Так что сами не позорьтесь.
          • 0
            окей, давайте разберёмся, кто чего не понимает

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

            кроме того, вы говорите «Нарушения безопасности нет. Потомучто довереный домен должен возвращать данные в определенном формате». В мире компьютерной безопасности это означает, что безопасность обеспечивается исключительно тем, что злоумышленник не знает формата, в котором передаются данные. Именно такой подход называется security through obscurity, и именно его ругают все подряд.
            • 0
              Нету задачи запутать злоумышленника. Стоит задача дать возможность кроссдоменных запросов для чистых JavaScript решений. Решается эта задача через хак — window.name транспорт. Есть еще одно решение через script транспорт. Оба хака не нарушают философию Same Origin Policy (изоляция сайта в пределах протокола, домена и порта) иначе бы их прикрыли. Потомучто для обоих хаков скрипты на которые производится кроссдоменный запрос изначально разрабатываются с поддержкой этих транспортов.

              • 0
                хм, если нет задачи запутать злоумышленника, почему вы вообще начали говорить про безопасность?
                • 0
                  Потомучто кроссдоменные запросы запрещены из-за соображений безопасности, и чтобы делать такие запросы приходится пользоваться хаками, но они не нарушают принцип Same Origin Policy. Поэтому вероятность что закроют хаки низка.
                  • 0
                    ага, спасибо за разъяснение, теперь понятно лучше, чем из первого вашего комментария. Он всё-таки больше напоминал о подходе «никто не знает, какой у меня алгоритм»: )
                • 0
                  потому что данный подход хоть и позволяет осуществлять кросс-доменные запросы в это же время не позволяет производить XSS скрабинг или как он называется по модному «Фишинг»
                  • 0
                    пусть не позволяет, ладно

                    но я уверен, что к первому моему комментарию такая реплика не имела отношения; )
  • 0
    про принцип работы я ошибся, да. Удивлён, что они стали так заморачиваться.

    фраза «100% сохраняется/работает» верна *на текущий момент* в тех браузерах, *которые вы тестировали*. Это может сломаться в ближайшем будущем, или не работать в редком браузере.

    а поддержки нового клёвого стандарта ждать долго: (
    • +2
      пока вы ждете и фантазируете на тему «как все плохо» — мы делаем нужные вещи и делимся с другими. Тех браузеров в которых мы тестировали достаточно с головой, потому что они перекрывают 95-99% pageview.
      • 0
        > пока вы ждете и фантазируете на тему «как все плохо»

        классический, но от этого не менее смелый ход в дискуссии — обвинить собеседника в только что придуманном грехе: )

        > достаточно с головой, потому что они перекрывают 95-99% pageview

        у меня большой опыт попадания в оставшиеся 1-5%, поэтому я и рекомендую использовать стандартные подходы
        • 0
          > у меня большой опыт попадания в оставшиеся 1-5%

          это как понимать? что у вас не работает наша тестовая страница?
          • 0
            сейчас ваша тестовая страница у меня работает. Но я не уверен, что так оно будет и дальше
            • 0
              а я не уверен что интрнет вообще будет дальше работать и что? я ж написал в начале
              фантазируете на тему «как МОЖЕТ БЫТЬ все плохо» когда в настоящий момент хорошо :)
              • 0
                а я уверен, что дальше интернет будет работать. Потому что он основан на открытых стандартах, которые может поддерживать любой человек. А полуслучайные совпадения, которые не описаны ни в каком публичном документе, могут внезапно исчезнуть, как уже не раз бывало. Недавно, например, закончилась поддержка Flash Paper, и никто ничего с этим не может поделать
                • 0
                  Начет закрытия хаков врятли они так поступят. Например, альтернатива window name хаку есть script. Известно что через тег script можно загружать скрипты с любого домена. Причем эта поддержка на уровне стандарта. Хак заключается в том что нужно вернуть валидный js код, а там уже будет содержаться ответ от сервера, например ответ от сервера такой, а клиентский код уже содержит функцию my_callback которая получит в аргументе ответ.

                  Чтобы прикрыть этот хак нужно запретить загрузку скриптов с другого домена. А это уже изменение на уровне стандарта. А если просто взять и запретить загрузку скриптов с другого домена может перестать работать множество сайтов (кто его знает сколько таких сайтов в сети, например у гугли есть целый сервис, который предоставляет скрипты популярных фреймворков, чтобы разработчики не хранили их у себя, а могли загрузить их с серверов гугли).
                  • 0
                    P.S. > например ответ от сервера такой <скрипт>my_callback('тут ответ в формате XML и т. п.')</скрипт>
                  • 0
                    оно понятно, что скрипты с другого домена будут работать как раньше. Вопрос только — при чем здесь window.name? Все эти аргументы к нему неприменимы.
                    • 0
                      Потомучто если будут закрывать window.name тогда надо и закрыть возможность получить кроссдоменные данные через script. Иначе одно закрыли, а другое нет, поэтому не будут трогать ни то ни другое.
                      • 0
                        нет уж, извините
                        вы сами сказали, что поведение script в спеке прописано (не хак), а поведение window.name — нет (хак). Именно поэтому первое не закроют, а второе могут закрыть.
                        • 0
                          а смысл? я не думаю что браузеры разрабатывают неумные люди, делают такие же програмеры как и мы, опасности в window.name нету значит и смысла закрывать нету, иначе уже б давно закрыли.
                          • 0
                            бывает, находят новые уязвимости, о которых не знали раньше
                            • 0
                              ну относительно window.name на данній момент уязвимости нет. если найдете — напишите статью, интересно будет почитать, я вам подкину пару вопросов, да и покритикую с удовольствием :)
                              • 0
                                да, сейчас нет

                                я изначально говорил именно о свойстве future-proof, которым стандартизованные вещи обладают в гораздо большей степени, чем подобные хаки
  • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      Вам наверно сюда: tema.livejournal.com
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Что-то мне подсказывает что туда идти не следует))) Да и нет привычки кликать по всем ссылкам)

          Может вам аналогичную ссылку на lleo подкинуть?
          • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Очень сильно извиняюсь, но я реализовывал динамический script по долгу службы еще прошлой осенью… в сентябре. Да и раньше это было и работало… и механизм с iFrame тогда существовал и работал…
    м? чего в этом нового? обертка в класс? хаафо, только были уже jQuery плагины для этого.
    • 0
      хорошо что извеняетесь :), потому что вы не внимательно прочитали даже заголовок — POST, основная соль в этом
      • 0
        * перечитывает… клепает тест… * о_О
        и правда. спать надо больше…
        не зря извинился) пардон.
  • 0
    Автору огромный респект, за обзор решения подобной проблемы, второй день парюсь с подобной проблемой
  • 0
    автору спасибо, помогла схема.
    Заметил одну такую особенность…
    если в срипте убрать вот эту строчку
    finish[id] = callback;
    то сервер на который отправляется запрос начинает изрядно подвисать.

    з.ы. возможно погрешности измерения
    • 0
      сервер начинает подвисать? а причем тут сервер, наверно вы что-то не так замеряли.
      • 0
        всмысле тот веб-сервер, где находится требуемая url

        2 раза запускал скрипт, и два раза на тот сайт становилось тяжело пробиться, возможно хостинг как то не адекватно реагировал… ну или в этот момент срабатывал чей-нибудь злой ддос ^)
        • 0
          уверен что проблема никак не связана с finish[id] = callback;

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.