ITsin ↓

Политика безопасности браузеров

Современные браузеры обладают рядом ограничений, о которых, безусловно, должен знать каждый разработчик. Наиболее часто web разработчики сталкиваются с политикой безопасности касательно кросс-доменных запросов "same origin policy"...

Суть данной политики заключается в том, что все скрипты, находящиеся на странице http://site1.com:80/ могут обмениваться информацией только с другими страницами из того же домена по тому же порту и тому же протоколу. Таким образом запросы к следующим адресам будут запрещены:

https://site1.com:80/ - другой протокол
http://site2.com:80 - другой домен
http://site1.com:8080/ - другой порт

При попытке обратиться к данной странице вы получите сообщение вроде этого:

XMLHttpRequest cannot load http://site2.com. Origin http://site1.com is not allowed by Access-Control-Allow-Origin.

С практической точки зрения данное ограничение означает, что вы не можете совершать AJAX запросы к другим доменам и не можете обращаться из вашего скрипта в IFRAME на вашей странице, принадлежащей другому сайту. Например, сервис ВКонтакте использует IFRAME для возможности вставки их системы комментариев к вам на сайт. Из-за политики безопасности вы не сможете из Javascript-а на вашей странице прочитать данные этого IFRAME-а и пройтись по комментариям или узнать имя пользователя ВКонтакте.

Данный запрет существует для того, чтобы некоторый зловредный сайт, на который вы зашли, не смог бы от вашего имени обратится например на сайт "www.gmail.com" и получить себе всю вашу почту. Данная политика просто запретит зловредному сайту, например, micrasoft.com, делать какие-либо запросы на сайт gmail.

Отмечу, что данная политика - дело браузера, то есть чисто теоретически браузер может её и не реализовывать. Такого, к счастью, нет среди известных браузеров, однако, сделать свой такой браузер вполне реально. Так же у многих браузеров есть возможность вручную отключить политику безопасности (обычно для целей отладки в процессе разработки). Например, чтобы отключить её в браузере Chrome (ну или Chromium) нужно запустить его со специальным ключом:

Для Windows/Linux:

chromium-browser --disable-web-security

Для Mac OS:

open -a Google\ Chrome --args --disable-web-security --allow-file-access-from-files

Порой для реализации определенного функционала все же необходима возможность крос-доменного взаимодействия. На этот случай в политике безопасности есть два исключения, на основе, которых все же можно реализовать обмен данными с другим сайтом. Исключениями являются теги <img> и <script> - они оба могут принимать в качестве значения атрибута src любой url и загружать его содержимое. То есть изображения и скрипты вы можете загружать с других доменов. На основе этих двух особенностей строятся различные методы "обхода" политики безопасности.

Обмен данными с другим сайтом можно разделить на два вида:

Первый случай используется, например, в Google Analitics (GA). Для того чтобы собирать статистику посещений с вашего сайта, ей необходимо передавать на сервера Google нужные сведения. В данном случае GA использует особенность тега <img> - создает временный невидимый элемент img и в качестве значения атрибута src устанавливает адрес однопиксельного gif изображения, передавая в параметрах GET-запроса (после символа &) необходимые пары ключ-значение:

http://www.google-analytics.com/__utm.gif?utmhn=www.ptweet.com&amp;utmcs=UTF-8&amp;utmsr=1680x1050&amp;utmvp=1680x552

В данном примере GA запрашивает с серверов Google изображение __utm.gif и передает в качестве параметров url посещенной страницы (utmhn=www.ptweet.com), мою кодировку (utmcs=UTF-8), текущее разрешение экрана (utmsr=1680x1050) и другие параметры, которые я для наглядности вырезал из запроса.

Второй случай использует особенность тега <script> для двустороннего обмена данными с другим сайтом. Данный метод называется JSONP (JSON with padding). JSON сам по себе представляет собой формат представления данных, который появился из недр Javascript-а, в следствии чего отлично им поддерживается. Пример данных описанных на JSON:

{
    "name": "Peter Parker",
    "nic": "Spiderman",
    "family": {
        "aunt": "May Reilly Parker-Jameson",
        "uncle": "Ben"       
    },
    "superpowers": [
        "Superhuman strength",
        "speed",
        "Regenerative healing factor",
        "Ability to cling to most surfaces"
    ]
}

Данный формат часто используется для передачи данных как альтернатива XML, в сравнении с которой JSON заметно более экономичен по трафику.

Суть метода JSONP заключается в следующем. Благодаря тому, что значением атрибута src тэга <script> может быть произвольный адрес, то мы готовим на стороне сервера site2.com специальный url, например:

http://site2.com/getData/77 ,

где 77 - это например id того элемента, который мы хотим получить. За данным url на стороне сервера стоит код, который делает следующее:

Наиболее неожиданным здесь является пункт 4. Суть его заключается в следующем. Если url site2.com/getData вернул бы нам в страницу просто преобразованные в формат JSON данные, то это вызвало бы синтаксическую ошибку при попытке браузера интерпретировать эти данные в javascript код. Из-за этого нам приходится формировать на стороне сервера скрипт с одной функцией, параметром которой является наш JSON объект. Итоговый, возвращаемый скрипт может быть например такого вида:

processData( {
    "name": "Peter Parker",
    "nic": "Spiderman",
    "family": {
        "aunt": "May Reilly Parker-Jameson",
        "uncle": "Ben"       
    },
    "superpowers": [
        "Superhuman strength",
        "speed",
        "Regenerative healing factor",
        "Ability to cling to most surfaces"
    ]
} );

Благодаря этому данный скрипт при загрузке к нам на страницу будет успешно интерпретирован javascript-движком браузера и вызовет функцию processData(), которая уже должна существовать у нас на странице, передав ей в параметры необходимую нам информацию, полученную с сервера http://site2.com/.

Таким образом можно организовать обмен данными между сайтами, которые попадают под запрет кросс-доменной политики браузеров. Для того, чтобы осуществлять такой обмен данными в необходимый нам момент времени нужно просто динамически создавать тэг <script>, генерируя для него соответствующий url.

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

Помимо описанных здесь способов существует еще рад других решений. Например, можно использовать flash, на который политика безопасности браузера не распространяется, однако у него есть своя схожая политика безопасности. Так же можно использовать java-апплеты и ряд других технологий, однако перечисленные в данной статье способы используются наиболее часто.