Технология CORS для XHR в Internet Explorer 10

Четвертая версия платформы Internet Explorer 10 упрощает построение межсайтовых сценариев, согласованно работающих в разных браузерах, благодаря поддержке технологии CORS (Cross-Origin Resource Sharing) для XMLHttpRequest (XHR). Технология CORS для XHR делает общий доступ сайтов к данным простым и гибким. В большинстве базовых сценариев технология CORS позволяет создавать источники данных, доступные с любого сайта, и с помощью нескольких небольших настроек вы можете ограничить перечень разрешенных сайтов, реализовать поддержку изменения данных и даже включить проверку подлинности. Чаще всего технология CORS обеспечивает безопасность существующих сайтов посредством запроса участия сервера.

Простой XHR-запрос о происхождении

Давайте рассмотрим, чем запрос XHR о происхождении отличается от запроса того же источника. По отношению к скрипту единственное отличие состоит в URL-адресе, передаваемом методу Open. Например, предположим, что мы работаем со скриптом, который вызывает список фотоальбомов.

Традиционный XHR

// Script running on http://photos.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "/albums", true);

xhr.send();

Теперь мы хотим получить доступ к списку альбомов из другого источника. Другой источник может быть совершенно другим доменом или другим узлом с таким же базовым доменом. В любом случае для того, чтобы браузер автоматически отправил запрос CORS, достаточно просто навести с другого сайта указатель на полный URL-адрес.

XHR с поддержкой CORS

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

Сайты могут предоставлять альтернативу для более старых браузеров, разместив ее в средстве обнаружения компонентов. Наилучший подход заключается в проверке свойства «withCredentials», так как оно непосредственно связано с поддержкой CORS для XHR.

XHR с поддержкой CORS и средством обнаружения компонентов

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

} else {

// Fallback behavior for browsers without CORS for XHR

}

На данном этапе наш клиентский код отправляет запрос CORS непосредственно на сайт «http://photos.contoso.com», однако этот запрос завершается сбоем и не возвращает никаких данных. Данный сбой происходит потому, что сервер еще не принимает участия. Быстро ознакомившись с информацией, представляемой средствами разработчика, можно понять причину ошибки.

Снимок экрана с информацией средств, вызываемых по нажатию клавиши F12, в которой сообщается об отсутствии заголовка 'Access-Control-Allow-Origin'.

Здесь мы видим, что сервер должен отправить в ответе заголовок «Access-Control-Allow-Origin». В данном сценарии мы не открываем альбомы для доступа с любого сайта, а хотим разрешить доступ исключительно с сайта «http://www.contoso.com». Для этого требуется разрешить серверу идентифицировать источник запроса. Анализ исходящего запроса позволяет выявить новый заголовок, содержащий требуемую информацию — «Origin».

Заголовки простых запросов CORS

GET http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

С помощью указанной информации сервер может ограничивать доступ любым набором сайтов. Если сервер всегда добавляет заголовок «Access-Control-Allow-Origin» со значением «*», то доступ будет предоставляться всем сайтам. Для данного сценария сервер должен проверить источник и затем установить «Access-Control-Allow-Origin», чтобы разрешить только «http://www.contoso.com».

Заголовки простых ответов CORS

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

Получив указанные выше обновления, наш клиент «http://www.contoso.com» теперь может осуществить доступ к спискам альбомов с сервера по адресу «http://photos.contoso.com».

XHR-запрос о происхождении с предварительным запросом

Рассмотренные ранее «простые» запросы CORS отлично подходят для базовых сценариев с доступом «только для чтения», таких как загрузка фотоальбома. Для реализации следующего этапа, заключающегося в межсайтовом изменении данных, требуется провести определенную работу на сервере. Например, предположим, что мы добавляем код в клиент для создания нового альбома.

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("PUT", "http://photos.contoso.com/albums", true);

xhr.send(JSON.stringify({ name: "New Album" }));

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

Снимок экрана средств, вызываемых по нажатию клавиши F12, на котором показан предварительный запрос OPTIONS.

В действительности браузер отправляет так называемый предварительный запрос. Предварительные запросы отправляются перед запросами, которые могут вызвать изменение данных на сервере. Такие запросы выявляются по наличию сложных свойств, как определено в спецификации CORS. Диапазон этих свойств варьируется от отдельных HTTP-методов, например «PUT», до настраиваемых HTTP-заголовков. Браузеры отправляют предварительные запросы, чтобы запросить у сервера разрешение на отправку непосредственного запроса. В данном примере браузер проверяет, разрешен ли запрос «PUT».

Предварительный запрос

OPTIONS http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

Access-Control-Request-Method: PUT

...

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

Снимок экрана с информацией средств, вызываемых по нажатию клавиши F12, в которой сообщается об отсутствии списка 'Access-Control-Allow-Methods'.

Первый этап заключается в том, чтобы заставить сервер распознавать предварительный запрос «OPTIONS» отдельно от других запросов для одного и того же URL-адреса. Когда сервер проверяет предварительный запрос, подтверждая, что «Access-Control-Request-Method» запрашивается для «PUT» из разрешенного источника, он отправляет соответствующее утверждение посредством заголовка «Access-Control-Allow-Methods».

Предварительный ответ

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

Access-Control-Allow-Methods: PUT

...

После отправки предварительного запроса и получения соответствующего утверждения выполняется непосредственный запрос.

Непосредственный запрос

PUT http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

На данном этапе технические аспекты добавления альбома выполнены, но код клиента не получит сведения об этом, пока сервер не отправит правильный ответ. Поэтому сервер по-прежнему должен включить «Access-Control-Allow-Origin» в ответ.

Непосредственный ответ

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

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

Последующие шаги

Связывание CORS с другими компонентами новой платформы позволяет реализовать интересные сценарии. Одним из примеров является Тест межсайтовой отправки, который отслеживает происхождение отправки файлов с использованием CORS, XHR, FileAPI и событий Progress.

— Тони Росс (Tony Ross), руководитель программы, Internet Explorer