Общение между окнами
Политика «Одинакового источника» (Same Origin) ограничивает доступ окон и фреймов друг к другу.
Политика «Одинакового источника»
Два URL имеют «одинаковый источник» в том случае, если они имеют совпадающие протокол, домен и порт.
Эти URL имеют одинаковый источник:
А эти – разные источники:
Политика «Одинакового источника» говорит, что:
Доступ к содержимому ифрейма
Мы можем обращаться к ним, используя свойства:
Для примера давайте попробуем чтение и запись в ифрейм с другим источником:
Код выше выведет ошибку для любых операций, кроме:
С другой стороны, если у ифрейма тот же источник, то с ним можно делать всё, что угодно:
…Но iframe.onload всегда доступно извне ифрейма, в то время как доступ к iframe.contentWindow.onload разрешён только из окна с тем же источником.
Окна на поддоменах: document.domain
По определению, если у двух URL разный домен, то у них разный источник.
Для этого в каждом таком окне нужно запустить:
После этого они смогут взаимодействовать без ограничений. Ещё раз заметим, что это доступно только для страниц с одинаковым доменом второго уровня.
Ифрейм: подождите документ
Когда ифрейм – с того же источника, мы имеем доступ к документу в нём. Но есть подвох. Не связанный с кросс-доменными особенностями, но достаточно важный, чтобы о нём знать.
Когда ифрейм создан, в нём сразу есть документ. Но этот документ – другой, не тот, который в него будет загружен!
Так что если мы тут же сделаем что-то с этим документом, то наши изменения, скорее всего, пропадут.
Нам не следует работать с документом ещё не загруженного ифрейма, так как это не тот документ. Если мы поставим на него обработчики событий – они будут проигнорированы.
Как поймать момент, когда появится правильный документ?
Можно проверять через setInterval :
Коллекция window.frames
Другой способ получить объект window из – забрать его из именованной коллекции window.frames :
Ифрейм может иметь другие ифреймы внутри. Таким образом, объекты window создают иерархию.
Навигация по ним выглядит так:
Атрибут ифрейма sandbox
Другими словами, если у атрибута «sandbox» нет значения, то браузер применяет максимум ограничений, но через пробел можно указать те из них, которые мы не хотим применять.
Вот список ограничений:
Больше опций можно найти в справочнике.
Обратите внимание, что ничего не работает. Таким образом, набор ограничений по умолчанию очень строгий:
Атрибут «sandbox» создан только для того, чтобы добавлять ограничения. Он не может удалять их. В частности, он не может ослабить ограничения, накладываемые браузером на ифрейм, приходящий с другого источника.
Обмен сообщениями между окнами
Интерфейс postMessage позволяет окнам общаться между собой независимо от их происхождения.
Интерфейс имеет две части.
postMessage
data Данные для отправки. Может быть любым объектом, данные клонируются с использованием «алгоритма структурированного клонирования». IE поддерживает только строки, поэтому мы должны использовать метод JSON.stringify на сложных объектах, чтобы поддержать этот браузер. targetOrigin Определяет источник для окна-получателя, только окно с данного источника имеет право получить сообщение.
Например, здесь win получит сообщения только в том случае, если в нём открыт документ из источника http://example.com :
Событие message
Чтобы получать сообщения, окно-получатель должно иметь обработчик события message (сообщение). Оно срабатывает, когда был вызван метод postMessage (и проверка targetOrigin пройдена успешно).
Исчерпывающий путеводитель по тегу iframe
Элемент iframe (сокращение от встроенного фрейма), вероятно, является одним из старейших тегов HTML и был представлен ещё в далеком 1997 году в HTML 4.01 Microsoft Internet Explorer.
Хотя все современные браузеры поддерживают данный тег, многие разработчики пишут бесконечные статьи, не советуя им пользоваться. Я считаю, что плохая репутация, которая сформировалась, около данного тега не должна мешать вам использовать его. У данного тега есть много хороших примеров применения. Кроме того, iframe не так сложно защитить, поэтому вам не придется беспокоиться о заражении компьютера вашего пользователя.
Чтобы помочь вам сформировать собственное мнение и отточить свои навыки разработчика, мы расскажем обо всем, что вам нужно знать об этом противоречивом теге.
Мы рассмотрим большинство функций, которые предоставляет элемент iframe, и поговорим о том, как их использовать, а также о том, как iframe может быть полезен для преодоления некоторых сложных ситуаций. Наконец, мы поговорим о том, как вы можете защитить свой iframe, чтобы избежать потенциальных уязвимостей.
Что такое iframe и зачем мы его используем?
Разработчики в основном используют тег iframe для встраивания другого HTML-документа в текущий.
Скорее всего, вы пересекались с ним, когда вам нужно было добавить сторонний виджет (например, знаменитую кнопку «Нравится» на Facebook), видео с YouTube, или рекламный раздел на вашем веб-сайте.
Например, приведенный ниже код будет отображать квадрат размером 500px с главной страницей Google:
Вот еще один пример, в котором мы показываем кнопку, чтобы твитнуть вашу веб-страницу в Twitter:
Когда вы думаете о iframe, вы должны иметь в виду, что он позволяет вам встраивать независимый HTML-документ с его контекстом просмотра.Таким образом, он будет изолирован от JavaScript и CSS родительского элемента. Это одна из веских причин использования iframe — обеспечить определенную степень разделения между вашим приложением и содержимым iframe.
Тем не менее, как вы увидите дальше в этом руководстве, разделение не так что б идеально.Iframe все еще может вести себя раздражающим или злонамеренным образом.
Как пример — вызывать всплывающее окно или автоматически воспроизводить видео.
(*Привет онлайн-казино, и сайтам любителей клубники )
Чтобы проиллюстрировать, насколько удобна эта изоляция от JavaScript и CSS, давайте взглянем на эти две ситуации:
2)Другая ситуация, когда iframe спасли мне жизнь, возникла, когда пришлось создавать WYSIWYG-редактор для клиента. Проблема была связана с тем, что при помощи этих редакторов вы должны найти способ сохранить возможность производить фокус и выделение, когда пользователь нажимает на любые кнопки в интерфейсе.
Поскольку iframe предлагает изолированную среду, это означает, что фокус или выделение никогда не теряются, когда вы щелкаете за его пределами. Используя события связи между iframe и родителем (подробнее об этом позже в этой статье), мне удалось в одно мгновение создать мощный редактор.
Атрибуты, которые стоило бы знать работая с iframe
На сегодняшний день существует восемь атрибутов, которые мы можем использовать для настройки поведения или стилизации iframe.
Вы можете найти больше, чем перечисленные выше, но имейте в виду, что они больше не поддерживаются в HTML5: align, frameborder, longdesc, marginheight, marginwidth и scrolling…
*Примечание. По умолчанию элемент iframe имеет рамку вокруг него. Чтобы удалить его, вы можете использовать атрибут style, чтобы установить свойство CSS border в none.
iframe события и общение
Загрузка и ошибки
Поскольку iframe является документом, вы можете использовать большинство глобальных обработчиков событий. Когда вы запускаете iframe, есть пара событий, которые могут пригодятся для улучшения работы. Например, отображение счетчика или отдельное сообщение, чтобы помочь пользователю:
load event — cобытие загрузки. Оно запускается, когда iframe полностью загружен. Другими словами, все статические ресурсы были загружены, и все элементы в дереве DOM вызвали событие load.
Событие ошибки — error event которое вызывается при сбое загрузки.
Вы можете прослушивать их с помощью атрибутов onload и onerror соответственно:
Если хотите, то вы можете добавить слушателей в свой iframe программно.
Общение с фреймами
Довольно легко отправлять сообщения между родителем и iframe. Вы должны использовать функцию postMessage, которая описана в MDN здесь.
От родителя к фрейму
Отправьте сообщение от родительского элемента:
И послушайте его в iframe:
От iframe к родителю
Отправьте сообщение из iframe:
И прослушай это у родителя:
Примечание: имейте в виду, что вы можете оказаться в некоторых хитрых ситуациях, когда вам нужно что-то отладить, так как сообщения запускаются и забываются (fire-and-forget) (иначе говоря. — нет реальной обработки ошибок).
Безопасность
Когда вы используете iframe, вы в основном имеете дело с контентом, полученным от третьей стороны, который вы не можете контролировать.
Таким образом, вы повышаете риск потенциальной уязвимости в вашем приложении или просто сталкиваетесь с плохим пользовательским интерфейсом (например, раздражающим автоматическим воспроизведением видео).
К счастью, вы можете занести в черный список или белый список конкретные функции.
Вы должны использовать sandbox (песочницу) и allow(допустимые) атрибуты.
! Помните, хорошее эмпирическое правило — всегда предоставлять минимальный уровень возможностей, необходимый ресурсу для его работы. Эксперты по безопасности называют эту концепцию «принципом наименьших привилегий».
Атрибут sandbox
Вот полный список флагов песочницы и их назначение:
| Флаг | Описание |
|---|---|
| allow-forms | Позволяет отправлять формы |
| allow-modals | Позволяет ресурсу открывать новые модальные окна |
| allow-orientation-lock | Позволяет ресурсу блокировать ориентацию экрана. |
| allow-pointer-lock | Позволяет ресурсу использовать API блокировки указателя (Pointer Lock API) |
| allow-popups | Позволяет ресурсу открывать новые всплывающие окна или вкладки. |
| allow-popups-to-escape-sandbox | Позволяет ресурсу открывать новые окна, которые не наследуют песочницу. |
| allow-presentation | Позволяет ресурсу начать сеанс презентации. |
| allow-same-origin | Позволяет ресурсу сохранять свое происхождение. |
| allow-scripts | Позволяет ресурсу запускать сценарии. |
| allow-top-navigation | Позволяет ресурсу перемещаться по контексту просмотра верхнего уровня. |
| allow-top-navigation-by-user-activation | Позволяет ресурсу перемещаться по контексту просмотра верхнего уровня, но только если он инициирован жестом пользователя. |
Вы несете ответственность, какие привилегии вы можете предоставить каждому iframe.
Например, если вашему iframe нужно только отправить формы и открыть новые модальные окна, вот как вы настроите атрибут песочницы:
В случае, когда атрибут песочницы настроен, а одна функция не работает правильно в ресурсе, это может быть связано с отсутствием определенного флага.
Убедитесь, что вы знаете о них больше, чтобы отладить вещи быстро.
Кроме того, имейте в виду, что использование пустого атрибута «песочница» полностью создаст «песочницу» для iframe.
Это означает, что JavaScript внутри iframe не будет выполняться, а все перечисленные выше привилегии будут ограничены (*например, создание новых окон или загрузка плагина).
Пустой атрибут песочницы в основном используется для статического содержимого, но это значительно снизит возможности, необходимые для правильной работы других ресурсов.
Примечание. Атрибут песочницы не поддерживается в Internet Explorer 9 и более ранних версиях.
Атрибут allow
Этот атрибут allow в настоящее время является экспериментальным и поддерживается только браузерами на основе Chromium. Это позволяет вам разрешить определенные функции белого списка, такие как доступ iframe к акселерометру, информации об аккумуляторе или камере.
Доступно более 25 флагов, поэтому я не буду перечислять их все здесь. Вы можете просмотреть их в документации по политике в отношении функциональных возможностей Mozilla.
Я суммировала самые популярные в таблице ниже:
| Флаг | Описание |
|---|---|
| accelerometer | Позволяет получить доступ к интерфейсу акселерометра |
| ambient-light-sensor | Позволяет получить доступ к интерфейсу AmbientLightSensor |
| autoplay | Позволяет автоматически воспроизводить видео и аудио файлы |
| battery | Разрешает доступ к API состояния батареи |
| camera | Позволяет доступ к камере |
| fullscreen | Предоставляет доступ к полноэкранному режиму |
| geolocation | Разрешает доступ к API геолокации |
| gyroscope | Предоставляет доступ к интерфейсу Sensors API Gyroscope. |
| magnetometer | Предоставляет доступ к интерфейсу магнитометра API датчиков |
| microphone | Предоставляет доступ к микрофону устройства |
| midi | Разрешает доступ к веб-MIDI API |
| payment | Разрешает доступ к API запроса платежа |
| usb | Разрешает доступ к API WebUSB |
| vibrate | Allows access to the Vibration API |
Что нужно знать о фреймах
Как решать проблемы с браузерами, которые не поддерживают фреймы
Таким образом, вы всегда должны думать о том, чтобы разместить предупреждающее сообщение как запасной вариант для этих пользователей.
Как вы можете сделать так, чтобы iframe был частью родительского документа (т.е. без границ и полос прокрутки)?
Для этой цели был введен бесшовный атрибут seamless. Он все еще экспериментален и плохо поддерживается в браузерах (это понимают только Chromium).
*Примечание Он также не является частью спецификации W3C HTML5 на момент написания этой статьи. Ознакомиться тут
Могут ли iframes повлиять на SEO моего сайта?
Я мало что знала об этом, поэтому мне пришлось немного покопаться. Есть много спекуляций вокруг этой темы.
Долгое время краулеры не могли понять содержимое iframes, но это уже не так. Наиболее релевантный ответ, который я нашла, был из этой статьи, и сегодняшний вывод выглядит так:
“Поскольку поисковые системы считают, что содержимое в фреймах принадлежит другому веб-сайту, лучшее, на что вы можете надеяться, — это отсутствие эффекта. Iframes, как правило, не помогают и не повреждают ваш рейтинг в поисковых системах. “
Таким образом, лучше всего предположить, что контент, отображаемый с помощью iframes, может быть не проиндексирован или недоступен для отображения в результатах поиска Google. Обходным путем может быть предоставление дополнительных текстовых ссылок на контент, который они отображают, чтобы робот Googlebot мог сканировать и индексировать этот контент.
Примечание. Вам также не следует беспокоиться о проблемах с дублированием контента, поскольку сканеры сегодня обычно их распознают.
Могут ли фреймы повлиять на скорость загрузки моего сайта?
Каждый iframe на странице увеличивает объем используемой памяти, а также другие вычислительные ресурсы, такие как пропускная способность.Поэтому не следует чрезмерно использовать iframe, не следя за тем, что происходит, иначе вы можете повредить производительности своей страницы.
Чтобы избежать того, что ваши фреймы замедляют работу ваших страниц, хорошим способом является их ленивая загрузка (т.е. загрузка их только тогда, когда они необходимы, например, когда пользователь прокручивает рядом с ними).Этого легко добиться, просто добавив в тег атрибут loading = «lazy».
Имейте в виду, что все современные браузеры на основе Chromium поддерживают это на момент написания этой статьи. Вас заинтересует библиотека lazyload, которая работает везде.
Примечание. Атрибут loading = «lazy» также работает с тегом img, если вы еще этого не знали.
Как я могу сделать iframe отзывчивым?
По мере того, как все больше людей просматривают Интернет с помощью своих телефонов, важно убедиться, что каждый из ваших интерфейсов отзывчивый.Но как это сделать, если на вашей странице есть фрейм?
Мы могли бы посвятить целое руководство относительно множества способов сделать ваш iframe отзывчивым. Вместо этого я просто сошлюсь на две отличные статьи:
Примечание. Если вы используете библиотеку начальной загрузки в своем проекте, есть embed-responsive и встроенные embed-responsive-16by9, которые вы можете использовать прямо из коробки, чтобы адаптировать свои фреймы.
Как предотвратить появление белой вспышки во время загрузки iframe
Да, друзья мои, для этого есть решение. В этой статье Крис Койер делится небольшим фрагментом, который скрывает все iframes на странице с помощью некоторого CSS-кода и удаляет его до тех пор, пока окно не будет загружено, а затем сделает их видимыми.
Как перезагрузить содержимое iframe
Легче лёгкого! Поскольку вы можете получить доступ к элементу окна iframe с помощью contentWindow, вы должны сделать это:
Я надеюсь, что это руководство помогло вам улучшить ваши знания о фреймах.
Хотя они могут быть небезопасными, если вы загружаете ненадежный контент, они также предлагают некоторые существенные преимущества. Поэтому вам не следует полностью исключать их из своего развивающегося арсенала, а использовать их только в соответствующих ситуациях.
Если у вас есть что добавить в эту статью, вы можете связаться со мной в комментариях ниже или просто пинговать меня в Twitter @RifkiNada
Как защитить ваш сайт с помощью песочницы HTML5
Черный ящик
Встраивание контента в IFRAME — это все равно, что публично объявить вечеринку в Facebook. Вы думаете, что знаете, кого пригласили, но на самом деле вы не представляете, кто это передал и кто появится.
То же самое верно для создания контента. Вы знаете, на что ссылаетесь, но понятия не имеете, как будет развиваться сайт в будущем. Содержание или функциональность (или оба) могут измениться в любое время. Без вашего ведома … и без вашего согласия.
Проблемы безопасности при использовании iframe
Браузеры обрабатывают страницы, которые используют IFRAME, как любую другую веб-страницу. Формы могут использоваться для извлечения пользовательского ввода, сценарии могут быть выполнены, страница может перемещаться в окне браузера, и могут быть выполнены плагины браузера. И так же, как нарушители вечеринок, которые выходят из-под контроля, вы не контролируете, что будет делать размещенный контент.
По умолчанию существует один механизм, предотвращающий некоторые виды атак: междоменная политика.
Повторное размещение контента из другого домена
Если размещенный контент поступает из другого домена, вступает в действие междоменная политика, которая запрещает «чужому» контенту доступ к объектной модели документа родителя.
Таким образом, встроенная страница не может читать, например, файлы cookie или локальное хранилище браузера для размещенного домена. Но риски все еще остаются.
Размещенный контент все еще может перемещаться на верхнем уровне. Отображая контент, который может ожидать пользователь, сайт может пытаться получить конфиденциальную информацию от пользователя. Или, используя подобную стилизованную форму, попытайтесь злонамеренно захватить пользовательскую информацию таким образом.
Вот почему даже при наличии междоменной политики все еще существуют большие угрозы безопасности.
Повторный хостинг контента из того же домена
Случай повторного размещения контента с того же домена еще хуже.
Когда контент поступает из того же домена, ограничения безопасности по умолчанию отсутствуют. Встроенный контент может получить доступ ко всему загруженному DOM браузера и манипулировать всем.
Это имеет смысл, что контент в одном домене должен быть безопасным. Риск здесь главным образом связан с пользовательским контентом, который повторно размещается в IFRAME.
Подход с песочницей
Эти действительные проблемы безопасности долгое время не были должным образом рассмотрены органом по стандартизации. Без четкого стандарта W3C было необходимо каким-то образом защитить хост от содержимого в рамке. Например, Microsoft предоставила проприетарную реализацию защиты IFRAME в Internet Explorer 8. Другие взяли ее на вооружение и обсудили как основу для своих браузеров. Но стандарты значительно выросли со времен IE8.
Давайте начнем с применения песочницы. Просто добавьте его как пустой атрибут в элемент IFRAME :
Это оно!
Теперь содержимое песочницы IFRAME повторно размещается в браузере со следующими ограничениями:
Это означает, что даже контент, поступающий из того же домена, обрабатывается междоменной политикой, поскольку каждый контент IFRAME будет рассматриваться как уникальный источник.
Встроенный контент разрешен только для отображения информации. Внутри IFRAME нельзя предпринимать никаких действий, которые могли бы поставить под угрозу хостинг веб-сайт или воспользоваться доверием пользователей.
Проверка атрибута песочницы
Мы знаем, что IFRAME — это открытые ворота. Мы знаем, что атрибут sandbox блокирует безопасность размещенного контента. Решение очевидно: используйте элементы IFRAME только с атрибутом sandbox!
Вы можете подтвердить, что браузер поддерживает атрибут песочницы IFRAME с помощью простой проверки в JavaScript:
Настройка Песочницы
Есть случаи, когда вам понадобится некоторый уровень настройки ограничений, что абсолютно возможно.
Несколько значений атрибутов ослабляют стандартную политику песочницы:
Доступ к контенту iFrame с другого домена
Сегодня я хочу рассказать о том, как мы в своем проекте indexisto.com сделали аналог инструмента Google Webmaster Marker. Напомню, что Marker это инструмент в кабинете Google Webmaster, который позволяет аннотировать ваши страницы Open Graph тегами. Для этого вы просто выделяете мышкой кусок текста на странице и указываете что это title, а это рейтинг. Ваша страница при этом грузится в Iframe в кабинете вебмастера.
Теперь Google, встретив подобную страницу на вашем сайте, уже знает, что за контент на ней опубликован, и как его красиво распарсить в сущность (статью, товар, видео..)
Нам был нужен подобный функционал. Задача казалась несложной и исключительно клиентсайд. Однако на практике решение лежит на стыке клиентсайда и серверсайда («чистые» JS программисты могу ничего не знать про различные прокси серверы и очень долго подходить к снаряду). При этом я не нашел в интернетах статью которая описывала бы всю технологию от начала до конца. Также хочется сказать спасибо пользователю BeLove и нашим безопасникам за помощь.
В нашем случае хотелось чтобы вебмастер мог удобно (пометив мышкой) получить значение xPath к определенным элементам на своей странице.
Iframe «Same Origin»
И так в нашей админке человек должен ввести URL страницы своего сайта, мы отобразим ее в iFrame, человек тыкнет мышкой куда надо, мы получим искомый xPath. Все бы ОК, но у нас нет доступа к контенту страницы с другого домена загруженной в iframe в нашей админке (наш домен), из за политики безопасности браузера.
CORS — Cross origin resource sharing
Одни люди мне посоветовали использовать CORS. Модная технология которая решает множество проблем с доступом к контенту с другого домена в браузере и позволяет обойти same origin policy ограничения.
Сайт который хочет дать доступ к своему контенту на страницах чужого домена просто пишет в http заголовок:
А в заголовоке http запроса идущего со страницы другого домена из браузера должно быть поле origin:
понятно что поле origin к запросу браузер дописывает сам. Плюсанем статью на Хабре и увидим что современные браузеры добавляют Origin даже к запросу на тот же самый домен:
Iframe sandbox
Еще одна модная технология. Sandbox это атрибут тега Iframe. В качестве одного из значений этого атрибута можно выставить значение allow-same-origin. До того как я начал копать эту тему я не знал что точно делает этот аттрибут, а звучало очень заманчиво. Однако атрибут sandbox просто ограничивает то, что может делать страница загруженная в iframe и не имеет отношения к проблеме доступа из родительского документа к содержимому фрейма.
Конкретно значение allow-same-origin (вернее его отсутствие) всего лишь говорит что iframe нужно всегда расценивать как загруженный с чужого домена (нельзя например из такого фрейма послать AJAX запрос на домен родительского документа)
Посмотрим как сделано у Google
Время рисеча того, как сделано у большого брата
Обратим внимание на аттрибут src элемента iframe: src=»https://wmthighlighter.googleusercontent.com/webmasters/data-highlighter/RenderFrame/007.» — наша страница грузится в админку с домена Google. Далее еще более сурово: даже скрипты и картинки в исходном документе прогоняются через прокси. Все src, href… заменены в html на проксированные. Примерно вот так:
Все ресурсы которые использует ваша страница еще и сохраняются на прокси серверах Гугл. Вот например наш логотип на прокси сервере Google.
CGIProxy?
Visit the script’s URL to start a browsing session. Once you’ve gotten a page through the proxy, everything it links to will automatically go through the proxy. You can bookmark pages you browse to, and your bookmarks will go through the proxy as they did the first time.
Свой Proxy!
Однако, если сузить задачу, написать простой proxy намного проще самому. Дело в том, что делать так делает Google, прогоняя весь контент страницы через прокси совершенно не обязательно. Нам просто нужно чтобы html любой страницы отдавался с нашего домена, а ресурсы можно подгрузить и из оригинального домена. Https мы пока отбросили.
Задача супер производительности или удобств настроек здесь не стоит, и сделать это можно по быстрому и на чем угодно, от node.js до php. Мы написали сервлет на Java.
Качаем страницу
Что должен делать прокси сервлет? Через get параметр получаем url страницы которую нужно загрузить, далее качаем страницу.
Обязательно определяем кодировку страницы (через http ответ или charset в html) — наш прокси должен ответить в той же кодировке что и страница которую мы загрузили. Так же определим Content-Type на всякий случай, хотя и так понятно что мы получаем страницу в text/html и отдадим ее так же.
*Для любителей оценить чужой код: у нас в команде у всех одинаковые настройки форматирования кода eclicpse, и при сохранении файла эклипс сам дописывает ко всем переменным final если они более нигде не меняются. Что кстати довольно удобно в итоге.
Меняем относительные URL на абсолютные в коде страницы
Надо пройтись по всем атрибутам с src и href в странице (пути файлов стилей, картинки), и заменить относительные урлы на абсолютные. Иначе страница будет пытаться загрузить картинки с каких-то папок на нашем прокси, которых у нас естественно нет. В любом языке есть готовые классы или можно найти сниппеты кода для этого дела на stackoverflow:
Отсылаем html
Вот и все, прокси сервлет готов. Посылаем ответ, выставив нужную кодировку и mime.
Деплоим и тестим
Деплоим наш прокси сервлет на том же адресе, что и админка adminpanel.indexisto.com, грузим в наш iframe страницу сайта вебмастера через прокси и все кроссдоменные проблемы исчезают.
Наш прокси работает по адресу
— вот так хабр загрузится с нашего домена. Отдаем этот адрес в iframe и пробуем получить доступ к DOM дереву хабра через JS в админке — все работает. CSRF естественно не пройдет так как страница загружена с нашего прокси у которого нет куки.
Проблемка SSRF
Загрузим в наш iframe сайт с адресом «localhost» — опа, вот стартовая страница нашего nginx. Попробуем какой-нибудь внутренний (не видный наружу) ресурс в той же сети, что и наш прокси сервер. Например secured_crm.indexisto.com — все на месте.
Конечно пытаемся запретить эти вещи в нашем прокси, в случае если кто-то пытается запроксировать localhost мы выходим ничего не возвращая:
но всех ресурсов сети мы здесь явно не перечислим. Значит надо вынести прокси в совершенно изолированную среду, чтобы машина ничего не видела кроме интернета, себя самой и нашего прокси. Выделяем машину, настраиваем и заводим наш сервлет там.
Проблемка XSS
Загрузим в наш iframe свою страничку на которой напишем:
Приплыли, мы разломали наше собственное решение с обходом кроссдоменных ограничений. Теперь мы снова не можем достучаться до контента iframe.
Интересная мысль.
Продолжая ресечить решение от гугла я открыл нашу страницу отдающуюся через прокси в отдельном окне
и обратил внимание на странную ошибку в консоли.
Стало ясно что все организованно сложнее, чем просто загрузить страницу в iframe со своего домена. Страницы общаются между собой. Соответственно двигаемся в сторону window.postMessage
Post Message
Заставлять внедрять вебмастера в свою страницу наш скрипт который бы обеспечивал выделение элементов страницы мышкой, а потом бы отсылал xPath этих элементов к нам в родительский документ через postMessage было не гуманно. Однако никто не мешает нашему прокси внедрить любые скрипты на загружаемую в iFrame страницу.
Все необходимые для внедрения скрипты сохраняем в файл, и вставляем их перед закрывающим body:
для пробы вставляем alert — все работает.
JS часть — подсвечиваем дом элемент под мышкой и получаем xpath
Окей, переходим собственно к JS который мы вставили на страницу вебмастера.
Нам необходимо подсвечивать dom элементы над которыми человек водит мышкой. Лучше делать это с помощью shadow так как тогда элемент не будет смещаться, а вся страница прыгать. Вешаем onmouseover на body и смотрим на target события. В этом же обработчике я вычисляю xpath элемента. Вычислять xPath элемента лучше на клик, но никаких тормозов и в такой реализации я не заметил.
Я не привожу здесь реализацию получения xPath элемента DOM. Существует множество сниппетов того как это сделать. Эти сниппеты можно модифицировать под свои задачи, например вам нужны в xpath только тэги. Или вам нужны id если они есть и классы если нет id — у всех свои требования.
JS часть — обрабатываем клик
Клик человека на странице в iframe сразу же «гасится» (перехода по ссылке в iframe не произойдет). А так же мы посылаем в родительское окно строку полученного xPath (мы его сохранили еще на этапе вождения мышкой над элементом)
Profit!
Вот и все, теперь в нашей админке вебмастер может намного проще быстро получить xpath пути к элементам на своих страницах.
Добавим еще секьюрности
Окей, у нас все заработало, однако есть момент с тем, что наш прокси смотрит в мир совершенно незащищенный. Кто угодно может запроксировать что угодно.
Ставим перед прокси nginx, он слушает 80 порт, сам прокси убираем на другой порт. Все другие порты кроме 80 закрываем от внешнего мира.
Теперь сделаем так чтобы прокси работал только через панель администратора. В момент когда вебмастер вводит URL своего сайта мы быстро бегаем на сервер где генерим md5 хэш от текущего TimeStamp + 1 час, самой URL и суперсекретного когда:
Так же обратите внимание что в коде мы получачаем md5 строку не как привычный hex, а в base64 кодировке, плюс в полученном md5 мы делаем странные замены символов слэша и плюса на подчеркивание и тире.
Дело в том что ngnix использует base64 Filename Safe Alphabet tools.ietf.org/html/rfc3548#page-6
А Java дает каноничсекий base64.
Получив в нашей админке ответ от сервера с секьюрной md5, мы пытаемся загрузить в iframe вот такой url:
highlighter.indexisto.com/?md5=Dr4u2Yeb3NrBQLgyDAFrHg==&url=http%3A%2F%2Fhabrahabr.ru&expires=1389791582
Теперь настраиваем модуль nginx HttpSecureLinkModule. Этот модуль сверяет md5 всех параметров которые к нему пришли (в модуле прописан тот же секретный ключ что и в сервлете админки), проверяет не проэкпарйилась ли ссылка и только в этом случае пробрасывает запрос на наш прокси сервлет.
Теперь никто не может воспользоваться нашим прокси не из админки, а так же не может куда-нибудь вставить запроксированную на наш сервер картинку — она все равно умрет через час.




