Междоменные проблемы, которые также необходимо понимать разработчикам веб-серверов

задняя часть сервер JavaScript браузер

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

В этой статье будут представлены некоторые концепции, связанные с междоменными запросами, и способы решения проблемы междоменных запросов браузера в серверной части (Python).

1. Что такое междоменный запрос

Во-первых, нам нужно понять, что такое кросс-оригинальный запрос. Проще говоря, когда ресурс сервера запрашивает ресурс с другого сервера (с другим доменным именем или портом), инициируется междоменный HTTP-запрос.

В качестве простого примераhttp://example-a.com/index.htmlЭта HTML-страница запрашиваетhttp://example-b.com/resource/image.jpgКогда этот ресурс изображения (инициирующий запрос Ajax, а не<img>тег), должен инициировать междоменный запрос.

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

2. Политика одинакового происхождения браузера

Это ведет кБраузерная политика того же происхождения (Same-origin policy), политика того же источника ограничивает взаимодействие документов или сценариев, загруженных из одного источника, с ресурсами из другого источника. Это важный механизм безопасности для изоляции потенциально вредоносных файлов.

Что такое гомология? Гомологичность должна соответствовать трем условиям одновременно:

  1. Запрашиваемый протокол одинаковы (например, тот же протокол HTTP)
  2. Запрошенное доменное имя совпадает (например,www.example.com)
  3. Запрошенный порт тот же (например, тот же порт 80)

Второй момент, на который стоит обратить внимание, это то, что доменное имя должно быть точно таким же, напримерblog.example.comиmail.example.comЭти два домена, хотя их домены верхнего и второго уровня (обаexample.com) одинаковы, но доменное имя третьего уровня (blogиmail) не совпадают, поэтому их нельзя считать одним и тем же доменным именем.

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

Исправлятьdocument.domainПараметры могут изменить текущий источник, например.blog.example.comхотите получить доступ к родительскому доменуexample.com, вы можете выполнить следующий сценарий JavaScript, чтобы изменить его:

1
2
document.domain = 'example.com';

ноdocument.domainне может быть установлено наfoo.comилиbar.com, потому что они неblog.example.comсупер домен.

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

  • Записи между источниками, такие как гиперссылки, перенаправления и отправка форм, требуют предварительной проверки нескольких HTTP-запросов;
  • Встраивание разных источников:
    • <script>Тег встроенный кросс-домен сценариев;
    • <link>CSS-файл, в который встроен тег;
    • <img>Пометить встроенное изображение;
    • <video>и<audio>Теги встраиваются в мультимедийные ресурсы;
    • <object>, <embed>, <applet>плагин;
    • @font-faceВнедрение шрифта, некоторые браузеры позволяют междоменные шрифты (перекрестные шрифты), шрифты, которые требуют гомологичных (шрифты одинакового происхождения);
    • <frame>и<iframe>Любой загруженный ресурс, который может использовать сайтX-Frame-Optionsзаголовки сообщений для организации этой формы междоменного взаимодействия.

Что делать, если в браузере отсутствует механизм безопасности политики того же источника? Представьте, когда вы входите в системуwww.bank.comКогда веб-сайт банка работает, браузер сохраняет информацию о файлах cookie при входе в систему. Если политика того же источника отсутствует, при посещении других веб-сайтов другие веб-сайты могут считывать информацию о файлах cookie, срок действия которых еще не истек, чтобы подделать операцию входа в систему. и привести к материальному ущербу.

3. CORS (междоменное совместное использование ресурсов, междоменное совместное использование ресурсов)

Хотя политика одного и того же источника в определенной степени обеспечивает безопасность, что, если это обычный запрос, который должен быть междоменным?

Существует четыре распространенных метода:

  1. JSONP
  2. <iframe>Этикетка
  3. CORS (совместное использование ресурсов из разных источников, совместное использование ресурсов из разных источников)
  4. прокси-сервер

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

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

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

3.1 Что такое КОРС

CORS на самом деле является спецификацией, сформулированной браузерами. Его реализация в основном на стороне сервера. Он ограничивает доступные домены через некоторые заголовки HTTP. Например, страница A должна получить доступ к данным на сервере B. Если сервер B объявляет, что A разрешен домен доступ по имени, то междоменный запрос от A к B может быть выполнен.

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

3.2 Простые запросы

Некоторые запросы не запускают предварительные запросы CORS, мы называем такие запросы простыми запросами.

Запрос может считаться простым запросом, если он соответствует всем следующим условиям:

  • GET, HEAD, POSTодин из способов;
  • Заголовок имеет только следующие поля:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Typeявляется одним из следующих трех:
      • text / plain`
      • multipart / form-data
      • application / x-www.form-urlencoded
    • DPR
    • Downloadlink
    • Save-Data
    • Viewport-Width
    • Width
  • любой в запросеXMLHttpRequestUploadНи один из объектов не зарегистрировал прослушивателей событий,XMLHttpRequestUploadобъект может использоватьXMLHttpRequest.uploadдоступ к собственности;
  • не используется в запросеReadableStreamобъект.

например1, например сайтhttp://foo.exampleвеб-приложений хотят получить доступhttp://bar.otherРесурсы,http://foo.exampleВеб-страницы могут содержать код JavaScript, аналогичный следующему:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
   
function callOtherDomain() {
  if(invocation) {    
    invocation.open('GET', url, true);
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

Студенты, знакомые с JavaScript, могут найти этот код дляhttp://bar.other/resources/public-data/инициировалGETСообщения запроса, запроса и ответа следующие.

Сообщение запроса:

2018-12-02-request-msg

Ответное сообщение:

2018-12-02-response-msg

В сообщении запросаOriginполе указывает, что запрос исходит отhttp://foo.example.

В ответном сообщении,Access-Control-Allow-Originполе установлено на*, что указывает на то, что к ресурсу может получить доступ любой домен.

использоватьOriginиAccess-Control-Allow-OriginМожно выполнить простейший контроль доступа. Если сервер разрешает толькоhttp://foo.exampleДоступ к домену должен быть установлен следующим образом:

1
Access-Control-Allow-Origin: http://foo.example

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

В отличие от простых запросов, сначала необходимо использовать требования «запросы с предварительной проверкой».OPTIONSЭтот метод отправляет на сервер предварительный запрос, чтобы узнать, разрешает ли сервер запрос или ему необходимо передавать информацию аутентификации. Использование «предварительного запроса» может предотвратить неожиданное влияние междоменных запросов на пользовательские данные сервера.

Если запрос соответствует всем следующим условиям, сначала необходимо отправить предварительный запрос.

  • Был использован любой из следующих методов HTTP:PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH;
  • Остальные поля, кроме поля Заголовок простого запроса, задаются в Заголовке (см. описание поля Заголовок в простом запросе);
  • Content-TypeЗначение не является одним из следующих:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • ЗапрошеноXMLHttpRequestUploadОбъект имеет любое количество зарегистрированных прослушивателей событий;
  • используется в запросеReadableStreamобъект.

Например следующий пример1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

Приведенный выше код отправляет документ XML с помощью запроса POST, который включает настраиваемое поле заголовка.X-PINGOTHER: pingpong. Кроме того, запрошенныйContent-Typeзаapplication/xml, поэтому запрос должен сначала инициировать «запрос предварительной проверки».

Сообщение запроса OPTIONS:

2018-12-03-option-request-msg

Ответное сообщение OPTIONS:

2018-12-03-option-response-msg

Метод OPTIONS — это метод, определенный в HTTP/1.1 для получения дополнительной информации с сервера, и этот метод не влияет на ресурсы сервера. Заголовки предварительного запроса содержат два поля:

1
2
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method: POSTполе сообщает серверу, что фактический запрос будет использоватьPOSTметод;Access-Control-Request-HeadersПоля сообщают серверу, что фактический запрос будет содержать два настраиваемых поля заголовка запроса:X-PINGOTHERиContent-Type, на основании которых сервер решает, разрешен ли фактический запрос.

Ответное сообщение OPTIONS указывает, что сервер будет принимать последующие фактические запросы, где:

1
2
3
4
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
  • Access-Control-Allow-Originпрямое разрешениеhttp://foo.exampleдомен для доступа;
  • Access-Control-Allow-MethodsУказывает, что клиенту разрешено отправлятьPOST,GET,OPTIONSпросить;
  • Access-Control-Allow-HeadersУказывает, что клиент порядка слов несетX-PINGOTHERиContent-TypeПоле заголовка;
  • Access-Control-Max-AgeУказывает, что ответ действителен в течение 86 400 секунд (24 часа). В течение этого времени браузеру не нужно повторно инициировать предварительный запрос для того же запроса. (Обратите внимание, что сам браузер поддерживает максимальное время действия, если поле заголовка превышает максимальное время действия, оно не вступит в силу).

После того, как предварительный запрос завершен, фактический запрос отправляется, и сообщение запроса выглядит следующим образом:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>

Ответное сообщение:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

3.4 Запросы с аутентификацией

В общем, для междоменногоXMLHTTPRequestилиFetchЗапрос, браузер не будет отправлять учетные данные удостоверения личности, если вам нужно отправить учетные данные удостоверения личности, вам нужно отправитьXMLHTTPRequestизwithCredentialsсвойство установлено наtrue.

Например1, следующий код представляетhttp://foo.exampleВ направленииhttp://bar.otherОтправитьGETзапросить и установитьCookies.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

поставивwithCredentialsУстановить какtrueтем самым посылаюCookiesзапрос. Потому что это простойGETзапрос, поэтому браузер не будет инициировать предварительный запрос, однако, если ответ сервера не содержитAccess-Control-Allow-Credentials: true, браузер не вернет содержимое ответа отправителю запроса.

Для запросов с аутентификацией сервер не должен устанавливатьAccess-Control-Allow-Originзначение*.

3.5 Заголовки для CORS

Ниже перечислены все поля заголовка, используемые в HTTP-запросах и ответах.Связанная документация.

Заголовки HTTP-запроса:

  • Origin: указывает исходный сайт предварительного запроса или фактического запроса, он не содержит никакой информации о пути, только имя сервера (URI);
  • Access-Control-Request-Method: используется для предварительных запросов, функция заключается в том, чтобы сообщить серверу метод HTTP, используемый фактическим запросом;
  • Access-Control-Request-Headers: используется для предварительных запросов, функция состоит в том, чтобы сообщить серверу поле заголовка, используемое фактическим запросом;

Заголовки ответа HTTP:

  • Access-Control-Allow-Origin: указывает URI внешнего домена, которому разрешен доступ к ресурсу;
  • Access-Control-Expose-Headers: разрешить серверу внести в белый список заголовки, к которым разрешен доступ браузеру, чтобы браузер мог использоватьgetResponseHeaderспособ доступа;
  • Access-Control-Max-Age: указывает, как долго результат предварительного запроса может кэшироваться;
  • Access-Control-Allow-Credentials: указывает, когда браузерcredentialsРазрешить ли браузер для чтения содержимого ответа при установке true;
  • Access-Control-Allow-Headers: ответ на предварительные запросы. Он определяет поля заголовка, которые разрешено использовать в фактическом запросе.

4. Серверная реализация

Для реализации CORS необходимо провести некоторую работу на стороне сервера, главное добавить указанные поля в заголовок ответа.

Если вы используете разработку Python + Flask, вы можете перейти кafter_app_requestДобавьте указанный заголовок ответа в функцию ловушки:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@app.after_app_request
def after_request(response):
    """正常请求结束后的处理"""
    # ... some code here
    
    response.headers['Access-Control-Allow-Origin'] = 'http://example.com'
    response.headers['Access-Control-Allow-Methods'] = 'GET, PUT, POST, DELETE, HEAD, OPTIONS'
    response.headers['Access-Control-Allow-Headers'] = (
        'Content-Type, Authorization, X-Requested-With'
    )
    
    # ... some code here
    
    return response

Другие языки можно обрабатывать в соответствующей функции хука.

использованная литература


  1. Источник для этого примера:Cross-Origin Resource Sharing (CORS) [return]