Понимание CORS

Правило одного источника является важным принципом обеспечения безопасности, реализованное web браузерами для предотвращения выполнения JavaScript кодом запросов к другим источникам(т.е. к другим доменам), а не к тому, с которого он передан. Несмотря на эффективность такого правила, оно также ограничивает в законных взаимодействиях между сервером и клиентами из надежных источников.

Cross-Origin Resource Sharing (CORS) является техникой для ослабления правила одного источника, позволяя JavaScript на web странице обрабатывать ответ от REST API от другого источника.

Обработка простых CORS запросов

В простейшем случае, междоменные взаимодействия начинаются с запроса GET, POST или HEAD к ресурсу на сервере. В данном случае, тип содержимого POST запроса ограничен application/x-www-form-urlencoded, multipart/form-data или text/plain. Запрос включает заголовок Origin, который указывает на происхождение клиентского кода.

Сервер будет учитывать Origin запроса и принимать или отказывать в обработке запроса. Если сервер принял запрос, то он ответит запрашиваемым ресурсом в заголовке Access-Control-Allow-Origin. Этот заголовок будет указывать клиенту с каким происхождением клиента будет разрешен доступ к ресурсу. Принимая во внимание, что Access-Control-Allow-Origin соответствует Origin запроса, браузер разрешит запрос.

С другой стороны, если Access-Control-Allow-Origin отсутствует в ответе или если его нет в Origin запроса, то браузер не разрешит запрос.

К примеру, предположим, что клиентский код расположен на foo.client.com и отправляет запрос на bar.server.com:

GET /greeting/ HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/536.30.1 (KHTML, like Gecko) Version/6.0.5 Safari/536.30.1
Accept: application/json, text/plain, */*
Referer: http://foo.client.com/
Origin: http://foo.client.com

Заголовок Originговорит серверу, что клиентский код создан в http://foo.client.com. Так он проверяет правила одного источника и определяет, что он может обработать запрос. Ответ может выглядеть примерно так:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Wed, 20 Nov 2013 19:36:00 GMT
Server: Apache-Coyote/1.1
Content-Length: 35
Connection: keep-alive
Access-Control-Allow-Origin: http://foo.client.com

[response payload]

Access-Control-Allow-Origin указывает на то, что "http://foo.client.com" разрешен доступ, но не другим источникам.

Дополнительно Access-Control-Allow-Origin может быть установлен в "*", указывая на доступность всем. Это считается небезопасной практикой, кроме тех особых случаев, где API полностью публично и предназначено для использования любым клиентом.

Предполетные запросы

Если запрос может оказать влияние на пользовательские данные, то простого запроса недостаточно. Вместо этого, предполентый CORS запрос отправляется в перед отправкой необходимого запроса, чтобы гарантировать безопасность отправки запроса. Предполетные запросы необходимы в тех случаях, когда любой HTTP метод, отличный от GET, POST, HEAD или если тип содержимого POST запроса отличен от application/x-www-form-urlencoded, multipart/form-data или text/plain. Также, если запрос содержит любые собственные заголовки, то необходим предполетный запрос.

Некоторые JavaScript библиотеки, такие как AngularJS или Sencha Touch, отправляют предполетные запросы на любой дочерний запрос. Этот подход пожалуй безопаснее, потому что он не предполагает, что сервис придерживается семантик HTTP метода(т.е. GET endpoint моглда быть написана с побочными эффектами).

К примеру, предположим, что клиент, расположенный на foo.client.com, выполняет DELETE запрос к ресурсу не bar.server.com. Предполетный запрос принимает вид OPTIONS запроса со следующими заголовками:

OPTIONS /resource/12345
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/536.30.1 (KHTML, like Gecko) Version/6.0.5 Safari/536.30.1
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with, accept
Origin: http://foo.client.com

Предполетный запрос фактически спрашивает сервер о доступности DELETE запроса без фактической отправки самого DELETE запроса. Если сервер разрешает такой запрос, то он ответит предполетному запросу примерно так:

HTTP/1.1 200 OK
Date: Wed, 20 Nov 2013 19:36:00 GMT
Server: Apache-Coyote/1.1
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: http://foo.client.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400

Ответ предполетному запросу указывает(в заголовке Access-Control-Allow-Methods) на то, что клиенту доступен DELETE запрос к передаваемому ресурсу. Заголовок Access-Control-Max-Age указывает на то, что этот предполетный ответ действует 84600 секунд или 1 день, после которого должен быть выполнен новый предполетный запрос.

В то же время клиенту будет доступно отправка настоящего DELETE запроса к ресурсу.

С оригинальным текстом урока вы можете ознакомиться на spring.io.

comments powered by Disqus