Кросс-доменные ограничения и их обход

Ограничение «Same Origin» («тот же источник») ограничивает доступ окон и фреймов друг к другу, а также влияет на AJAX-запросы к серверу.

Причина, по которой оно существует – безопасность. Если есть два окна, в одном из которых vasya-pupkin.com, а в другом gmail.com, то мы бы не хотели, чтобы скрипт из первого мог читать нашу почту.

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

Концепция Same Origin

Два URL считаются имеющим один источник («same origin»), если у них одинаковый протокол, домен и порт.

Эти URL имеют один источник:

  • http://site.com
  • http://site.com/
  • http://site.com/my/page.html

А вот эти – все из других источников:

  • http://www.site.com (другой домен)
  • http://site.org (другой домен)
  • https://site.com (другой протокол)
  • http://site.com:8080 (другой порт)

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

В действии

Если одно окно попытается обратиться к другому, то браузер проверит, из одного ли они источника. Если нет – доступ будет запрещён.

Например:

<iframe src="https://example.com"></iframe>

<script>
  var iframe = document.body.children[0];

  iframe.onload = function() {
    try {
      alert( iframe.contentWindow.document );
    } catch (e) {
      alert( "Ошибка: " + e.message );
    }
  }
</script>

Пример выше выведет ошибку.

Исключение: запись в location

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

Причём читать свойства location нельзя, одно окно не имеет право знать, на каком URL пользователь в другом. А вот запись браузеры считают безопасной.

Например, открыв на javascript.ru iframe с http://example.com, из этого ифрейма нельзя будет прочитать URL, а вот поменять его – запросто:

<iframe src="https://example.com"></iframe>

<script>
  var iframe = document.body.children[0];

  iframe.onload = function() {
    try {
      // не сработает (чтение)
      alert( iframe.contentWindow.location.href );
    } catch (e) {
      alert( "Ошибка при чтении: " + e.message );
    }

    // сработает (запись)
    iframe.contentWindow.location.href = 'https://wikipedia.org';

    iframe.onload = null;
  }
</script>

Если запустить код выше, то окно сначала загрузит example.com, а потом будет перенаправлено на wikipedia.org.

Исключение: поддомен 3-го уровня

Ещё одно важное исключение касается доменов третьего уровня.

Если несколько окон имеют общий домен второго уровня, к примеру john.site.com, peter.site.com, site.com, и присваивают в document.domain свой общий поддомен 2-го уровня site.com, то все ограничения снимаются.

То есть, на всех этих сайтах должен быть код:

document.domain = 'site.com';

Тогда между ними не будет кросс-доменных ограничений.

Обратим внимание: свойство document.domain должно быть присвоено на всех окнах, участвующих в коммуникации. Выглядит абсурдно, но даже на документе с site.com нужно вызвать: document.domain="site.com". Иначе не будет работать.

Таким образом разные подсайты в рамках одного общего проекта могут взаимодействовать без ограничений.

Исключения в IE

В браузере Internet Explorer есть два своих, дополнительных исключения из Same Origin Policy.

  1. Порт не входит в понятие «источник» (origin).

    Это означает, что окно с http://site.com может свободно общаться с http://site.com:8080.

    Это иногда используют для общения серверов, использующих один IP-адрес. Но допустимо такое только в IE.

  2. Если сайт находится в зоне «Надёжные узлы», то в Internet Explorer ограничения к нему не применяются.

    При этом подразумевается, что для этой зоны в параметрах «Безопасность» включена опция «Доступ к источникам данных за пределами домена».

Итого

Ограничение «одного источника» запрещает окнам и фреймам с разных источников вызывать методы друг друга и читать данные друг из друга.

При этом «из одного источника» означает «совпадают протокол, домен и порт».

У этого подхода ряд существенных исключений:

  • Свойства window.location.* нельзя читать, но можно менять.
  • Домены третьего уровня с общим наддоменом могут поменять document.domain на их общий домен второго уровня, и тогда они смогут взаимодействовать без ограничений.
  • IE не включает порт в понятие источника. Кроме того, он позволяет снять ограничения для конкретного сайта включением в доверенную зону.
Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.