Same origin policy что это
Кросс-доменные ограничения и их обход
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/cross-window-communication.
Ограничение «Same Origin» («тот же источник») ограничивает доступ окон и фреймов друг к другу, а также влияет на AJAX-запросы к серверу.
Сама концепция проста, но есть много важных исключений и особенностей, которые нужно знать для полного понимания этого правила.
Концепция Same Origin
Два URL считаются имеющим один источник («same origin»), если у них одинаковый протокол, домен и порт.
Эти URL имеют один источник:
А вот эти – все из других источников:
Существует ряд исключений, позволяющих-таки окнам с разных доменов обмениваться информацией, но прямой вызов методов друг друга и чтение свойств запрещены.
В действии
Если одно окно попытается обратиться к другому, то браузер проверит, из одного ли они источника. Если нет – доступ будет запрещён.
Пример выше выведет ошибку.
Исключение: запись в location
Окна могут менять location друг друга, даже если они из разных источников.
Причём читать свойства location нельзя, одно окно не имеет право знать, на каком URL пользователь в другом. А вот запись браузеры считают безопасной.
Исключение: поддомен 3-го уровня
Ещё одно важное исключение касается доменов третьего уровня.
То есть, на всех этих сайтах должен быть код:
Тогда между ними не будет кросс-доменных ограничений.
Таким образом разные подсайты в рамках одного общего проекта могут взаимодействовать без ограничений.
Исключения в IE
В браузере Internet Explorer есть два своих, дополнительных исключения из Same Origin Policy.
Порт не входит в понятие «источник» (origin).
Это иногда используют для общения серверов, использующих один IP-адрес. Но допустимо такое только в IE.
Если сайт находится в зоне «Надёжные узлы», то в Internet Explorer ограничения к нему не применяются.
При этом подразумевается, что для этой зоны в параметрах «Безопасность» включена опция «Доступ к источникам данных за пределами домена».
Итого
Ограничение «одного источника» запрещает окнам и фреймам с разных источников вызывать методы друг друга и читать данные друг из друга.
При этом «из одного источника» означает «совпадают протокол, домен и порт».
У этого подхода ряд существенных исключений:
Что такое CORS
Многие из нас встречались с подобной ошибкой:
Access to XMLHttpRequest at ‘XXXX’ from origin ‘YYYY’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource..
Эта статья рассказывает что означает эта ошибка и как от нее избавиться.
Создадим тестовый сайт на Node.js с открытым API и запустим его по адресу http://127.0.0.1:3000.
Пусть там будет примерно такая функция получения GET запроса:
Пусть там будет простая функция входа в систему, где пользователи вводят общее секретное слово secret и им затем ему устанавливается cookie, идентифицируя их как аутентифицированных:
И пусть у нас будет некое приватное API для каких нибудь личных данных в /private, только для аутентифицированных пользователей.
Запрос нашего API через AJAX из других доменов
И допустим у нас есть какое-нибудь клиентское приложение работающее с нашим API. Но учтем что, наше API находится по адресу http://127.0.0.1:3000/public, а наш клиент размещен на http://127.0.0.1:8000, и на клиенте есть следующий код:
И это не будет работать!
Если мы посмотрим на вкладку network в консоле Хрома при обращение c http://127.0.0.1:8000 к http://127.0.0.1:3000 то там не будет ошибок:
Сам по себе запрос был успешным, но результат оказался не доступен. Описание причины можно найти в консоли JavaScript:
Ага! Нам не хватает заголовка Access-Control-Allow-Origin. Но зачем он нам и для чего он вообще нужен?
Same-Origin Policy
Например: если вы заходите на sample.org, вы бы не хотели, чтобы этот веб-сайт отправлял запрос к примеру на ваш банковский веб-сайт и получал баланс вашего счета и транзакции.
Same-Origin Policy предотвращает именно это.
«источник (origin)» в этом случае состоит из
Так что http://sample.org и http://www.sample.org и http://sample.org:3000 — это три разных источника.
Пару слов о CSRF
Обратите внимание, что существует класс атак, называемый подделкой межсайтовых запросов (Cross Site Request Forgery — csrf ), от которых не защищает Same-Origin Policy.
При CSRF-атаке злоумышленник отправляет запрос сторонней странице в фоновом режиме, например, отправляя POST запрос на веб-сайт вашего банка. Если у вас в этот момент есть действительный сеанс с вашим банком, любой веб-сайт может сгенерировать запрос в фоновом режиме, который будет выполнен, если ваш банк не использует контрмеры против CSRF.
Так же обратите внимание, что, несмотря на то, что действует Same-Origin Policy, наш пример запроса с сайта secondparty.com на сайте 127.0.0.1:3000 будет успешно выполнен — мы просто не соможем получить доступ к результатам. Но для CSRF нам не нужен результат …
Например, API, которое позволяет отправлять электронные письма, выполняя POST запрос, отправит электронное письмо, если мы предоставим ему правильные данные. Злоумышленнику не нужно заботится о результате, его забота это отправляемое электронное письмо, которое он получит независимо от возможности видеть ответ от API.
Включение CORS для нашего публичного API
Допустим нам нужно разрешить работу JavaScript на сторонних сайтах (например, 127.0.0.1:8000) что бы получать доступ к нашим ответам API. Для этого нам нужно включить CORS в заголовок ответа от сервера. Это делается на стороне сервера:
Здесь мы устанавливаем заголовку Access-Control-Allow-Origin значение *, что означает: что любому хосту разрешен доступ к этому URL и ответу в браузере:
Непростые запросы и предварительные запросы (preflights)
Предыдущий пример был так называемым простым запросом. Простые запросы — это:
Допустим теперь 127.0.0.1:8000 немного меняет реализацию, и теперь он обрабатывает запросы в формате JSON:
Но это снова все ломает!
На этот раз консоль показывает другую ошибку:
Любой заголовок, который не разрешен для простых запросов, требует предварительного запроса (preflight request).
Этот механизм позволяет веб-серверам решать, хотят ли они разрешить фактический запрос. Браузер устанавливает заголовки Access-Control-Request-Headers и Access-Control-Request-Method, чтобы сообщить серверу, какой запрос ожидать, и сервер должен ответить соответствующими заголовками.
Но наш сервер еще не отвечает с этими заголовками, поэтому нам нужно добавить их:
Теперь мы снова может получить доступ к ответу.
Credentials и CORS
Теперь давайте предположим, что нам нужно залогинится на 127.0.0.1:3000 что бы получить доступ к /private с конфиденциальной информацией.
При всех наших настройках CORS может ли другой сайт так же получить эту конфиденциальную информацию?
Мы пропустили код реализации входа в на сервер так как он не обязателен для объяснения материала.
Независимо от того, попытаемся ли мы залогинится на 127.0.0.1:3000 или нет, мы увидим «Please login first».
Причина в том, что cookie от 127.0.0.1:3000 не будут отправляться, когда запрос поступает из другого источника. Мы можем попросить браузер отправить файлы cookie клиенту, даже если запрос с других доменов:
Но опять это не будет работать в браузере. И это хорошая новость, на самом деле.
Итак, мы не хотим, чтобы злоумышленник имел доступ к приватным данным, но что, если мы хотим, чтобы 127.0.0.1:8000 имел доступ к /private?
В этом случае нам нужно установить для заголовка Access-Control-Allow-Credentials значение true:
Но это все равно пока еще не сработает. Это опасная практика — разрешать любые аутентифицированные запросы с других источников.
Браузер не позволит нам так легко совершить ошибку.
Если мы хотим разрешить 127.0.0.1:8000 доступ к /private, нам нужно указать точный источник в заголовке:
Теперь http://127.0.0.1:8000 также имеет доступ к приватным данным, в то время как запрос с любого другого сайта будет заблокирован.
Разрешить множественные источники (origin)
Теперь мы разрешили одному источнику делать запросы к другому источнику с данными аутентификации. Но что, если у нас есть несколько других источников?
В этом случае мы, вероятно, хотим использовать белый список:
Опять же: не отправляйте напрямую req.headers.origin в качестве разрешенного заголовка CORS. Это позволит любому веб-сайту получить доступ к приватным данным.
Из этого правила могут быть исключения, но, по крайней мере, дважды подумайте, прежде чем внедрять CORS с учетными данными без белого списка.
Заключение
В этой статье мы рассмотрели Same-Origin Policy и то, как мы можем использовать CORS, чтобы разрешать запросы между источниками, когда это необходимо.
Это требует настройки на стороне сервера и на стороне клиента и в зависимости от запроса вызовет предварительный (preflight) запрос.
При работе с аутентифицированными запросами перекрестного происхождения следует проявлять дополнительную осторожность. Белый список может помочь разрешить нескольким источникам без риска утечки конфиденциальных данных (которые защищены аутентификацией).
Выводы
Безопасность наглядно: CORS
В этой статье я не буду останавливаться на основах HTTP. Скажу лишь, что в своих примерах я использую HTTP/1.1, а не HTTP/2 — на CORS это никак не влияет.
Во фронтенде часто требуется отобразить данные, которые хранятся в другом месте. Перед этим браузер должен направить запрос серверу: клиент отправляет HTTP-запрос со всей информацией, которая нужна серверу, чтобы вернуть данные.
Что произошло? Мы отправили такой же запрос, но на этот раз браузер выдал странную ошибку. Мы только что увидели CORS в действии. Давайте разберёмся, почему возникла эта ошибка, и что она означает.
Правило одинакового источника (Same-Origin Policy)
Ресурс считается принадлежащим к другому источнику (cross-origin), если он располагается на другом домене/поддомене, протоколе или порте.
Это, конечно, здорово, но для чего правило одинакового источника вообще существует?
Представим, что это правило не работает, а вы случайно нажали на какую-то вирусную ссылку, которую прислала ваша тётушка на Фейсбуке. Ссылка перенаправляет вас на мошеннический сайт, который с помощью фрейма загружает интерфейс сайта вашего банка и успешно залогинивает вас с помощью сохранённых куки.
Разработчики этого мошеннического сайта сделали так, чтобы он имел доступ к фрейму и мог взаимодействовать с DOM сайта вашего банка — так они смогут переводить деньги на свой счёт от вашего имени.
Да, это огромная угроза безопасности — мы ведь не хотим, чтобы кто угодно имел доступ к чему угодно.
К счастью, здесь приходит на помощь правило одинакового источника: оно гарантирует, что мы можем получить доступ только к ресурсам из того же самого источника.
Но какое отношение всё это имеет к CORS?
CORS на стороне клиента
Несмотря на то, что правило одинакового источника применяется исключительно к скриптам, браузеры распространили его и на JavaScript-запросы: по умолчанию можно получить доступ к ресурсам только из одинакового источника.
Но нам ведь часто нужно обращаться к ресурсам из других источников… Может, тогда фронтенду стоит взаимодействовать с API на бэкенде, чтобы загружать данные? Чтобы обеспечить безопасность запросов к другим источникам, браузеры используют механизм под названием CORS.
Аббревиатура CORS расшифровывается как Cross-Origin Resource Sharing (Технология совместного использования ресурсов между разными источниками). Несмотря на то, что браузеры не позволяют получать доступ к ресурсам из разных источников, можно использовать CORS, чтобы внести небольшие коррективы в эти ограничения и при этом быть уверенным, что доступ будет безопасным.
Пользовательские агенты (к примеру, браузеры) на основе значений определённых заголовков для CORS в HTTP-запросе могут проводить запросы к другим источникам, которые без CORS были бы заблокированы.
Когда происходит запрос к другому источнику, клиент автоматически подставляет дополнительный заголовок Origin в HTTP-запрос. Значение этого заголовка отражает источник запроса.
Чтобы браузер разрешил доступ к ресурсам из другого источника, он должен получить определённые заголовки в ответе от сервера, которые указывают, разрешает ли сервер запросы из других источников.
CORS на стороне сервера
Да, теперь CORS выдаёт эту печально известную ошибку, которая иногда всех нас так расстраивает. Но сейчас нам понятно, какой смысл она несет.
В качестве значения разрешённых источников CORS позволяет указать спецсимвол * . Он означает, что доступ к ресурсам открыт из всех источников, поэтому используйте его с осторожностью.
Если вам интересно почитать о других CORS-заголовках, ознакомьтесь с их списком на MDN.
Предварительные запросы
Существует два типа CORS-запросов: простые и предварительные. Тип запроса зависит от хранящихся в нём значений (не волнуйтесь, здесь не надо будет ничего запоминать).
Если интересно узнать, каким требованиям должен соответствовать запрос, чтобы называться простым, почитайте эту статью на MDN.
Но что означают и почему существуют “предварительные запросы”?
Перед отправкой текущего запроса клиент сначала генерирует предварительный запрос: в своих заголовках Access-Control-Request-* он содержит информацию о текущем запросе. Это позволяет серверу узнать метод, дополнительные заголовки и другие параметры запроса, который браузер пытается отправить.
Сервер получает этот предварительный запрос и отправляет обратно пустой HTTP-ответ с CORS-заголовками сервера. Браузер в свою очередь получает предварительный ответ (только CORS-заголовки) и проверяет, разрешён ли HTTP-запрос.
Если всё в порядке, браузер посылает текущий запрос на сервер, а тот в ответ присылает данные, которые мы запрашивали.
Если же возникает проблема, CORS блокирует предварительный запрос, а текущий вообще уже не будет отправлен. Предварительный запрос — отличный способ уберечь нас от получения доступа или изменения ресурсов на серверах, у которых (пока что) не настроены правила CORS. Сервера защищены от потенциально нежелательных запросов из других источников.
Чтобы уменьшить число обращений к серверу, можно кэшировать предварительные ответы, добавив к CORS-запросам заголовок Access-Control-Max-Age . Так браузеру не придётся каждый раз отправлять новый предварительный запрос.
Учётные данные
Куки, заголовки авторизации, TLS-сертификаты по умолчанию включены только в запросы из одинакового источника. Однако нам может понадобиться использовать учётные данные и в запросах из разных источников. Возможно, мы захотим включить куки в запрос, который сервер сможет использовать для идентификации пользователя.
Готово — теперь мы можем включать учётные данные в запрос из другого источника.
Думаю, мы все согласимся с тем, что появление ошибок CORS порой расстраивает, но, тем не менее, здорово, что CORS позволяет безопасно отправлять запросы из разных источников в браузере — считаю, что мы должны любить эту технологию чуточку сильнее 🙂
Разумеется, о правиле одинакового источника и CORS можно рассказать гораздо больше, но я просто не смогу уместить всё это в одной статье. К счастью, ресурсов много (к примеру, спецификация W3) — вам будет к чему обратиться, если захотите подробнее изучить эту тему.
Same-origin policy
Политика одинакового источника (same-origin policy) определяет как документ или скрипт, загруженный из одного источника (origin), может взаимодействовать с ресурсом из другого источника. Это помогает изолировать потенциально вредоносные документы, снижая количество возможных векторов атак.
Определение origin
В следующей таблице даны примеры origin-сравнений с URL http://store.company.com/dir/page.html :
URL | Outcome | Reason |
---|---|---|
http://store.company.com/dir2/other.html | Success | |
http://store.company.com/dir/inner/another.html | Success | |
https://store.company.com/secure.html | Failure | Different protocol |
http://store.company.com:81/dir/etc.html | Failure | Different port |
http://news.company.com/dir/other.html | Failure | Different host |
Наследование origins
Контент из about:blank и javascript: адреса наследуют источник документа, содержащего этот URL, поскольку они не содержат информации о сервере происхождения.
Например, about:blank часто используется в качестве URL новых, пустых окон в которые родительский скрипт записывает контент (например, с помощью Window.open() (en-US) ). Если это окно также содержит JavaScript, то скрипт будет наследовать то же происхождение, что и его родитель.
data: адреса получают новый, пустой, безопасный контекст.
Исключения в Internet Explorer
Internet Explorer два основных исключения из политики одно происхождения:
Trust Zones (Зоны доверия) Если оба домена находятся в зоне высокого доверия (например, зоны корпоративной интрасети), то ограничения на одно и то же происхождение не применяется. Порт IE не включает порт в same-origin проверку. Следовательно, https://company.com:81/index.html и https://company.com/index.html являются адресами одного происхождения и ограничения действовать не будут.
Эти исключения являются нестандартными и не поддерживаются в любом другом браузере
Изменение origin
Страница может изменить свой оригинальный origin с некоторыми ограничениями. Скрипт может установить значение document.domain на текущий домен или супердомен текущего домена. Если значение будет установлено на супердомен текущего домена, более короткий домен будет использован для последующей проверки origin’а. Например, предполагается, что скрипт в документе по адресу http://store.company.com/dir/other.html выполняется следующим выражением:
Замечание: Когда используется document.domain для разрешения поддомена для родительского доступа к нему, вы должны установить document.domain в такое же значение, как в родительском домене, так и в поддомене. Это необходимо, даже если при этом просто восстанавливается исходное значение родительского домена. Несоблюдение этого правила может привести к ошибкам разрешений.
Cross-origin network access
The same-origin policy controls interactions between two different origins, such as when you use XMLHttpRequest or an element. These interactions are typically placed into three categories:
Here are some examples of resources which may be embedded cross-origin:
How to allow cross-origin access
Use CORS to allow cross-origin access.
How to block cross-origin access
Use CORS to allow cross-origin access.
Доступ к контенту 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 всех параметров которые к нему пришли (в модуле прописан тот же секретный ключ что и в сервлете админки), проверяет не проэкпарйилась ли ссылка и только в этом случае пробрасывает запрос на наш прокси сервлет.
Теперь никто не может воспользоваться нашим прокси не из админки, а так же не может куда-нибудь вставить запроксированную на наш сервер картинку — она все равно умрет через час.