XMLHttpRequest не может загрузить XXX Нет заголовка "Access-Control-Allow-Origin"

ТЛ; др; О той же политике происхождения

У меня есть процесс Grunt, который инициирует экземпляр сервера express.js. Это работало абсолютно нормально до тех пор, пока только не начал обслуживать пустую страницу, а в журнале ошибок в консоли разработчика в Chrome (последняя версия) появилось следующее:

XMLHttpRequest не может загрузить https://www.example.com/ На запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Источник ' http://localhost: 4300 ', следовательно, не имеет доступа.

Что мешает мне получить доступ к странице?

Ответ 1

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

О той же политике происхождения

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

Ваш конкретный случай показывает, как он реализован для XMLHttpRequest (и вы получите идентичные результаты, если будете использовать fetch), но это также относится и к другим вещам (таким как изображения, загруженные в <canvas> или документы, загруженные в <iframe>), только с немного другими реализациями.

(Как ни странно, это также относится к CSS-шрифтам, но это потому, что найденные литейщики настаивали на DRM, а не на проблемах безопасности, которые обычно покрывает та же политика происхождения).

Стандартный сценарий, демонстрирующий необходимость в SOP, можно продемонстрировать с помощью трех символов:

  • Алиса - человек с веб-браузером
  • Боб ведет веб-сайт (https://www.[website].com/ в вашем примере)
  • Мэллори ведет веб-сайт (http://localhost:4300 в вашем примере)

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

Алиса посещает веб-сайт Мэллори, на котором есть некоторый JavaScript, который заставляет браузер Алисы отправлять HTTP-запрос на веб-сайт Боба (с ее IP-адреса с помощью файлов cookie и т.д.). Это может быть так же просто, как использовать XMLHttpRequest и прочитать responseText.

Браузер Same Origin Policy запрещает JavaScript читать данные, возвращаемые веб-сайтом Боба (которые Боб и Алиса не хотят, чтобы Мэллори получал доступ). (Обратите внимание, что вы можете, например, отображать изображение с использованием элемента <img> в разных источниках, потому что содержимое изображения не подвергается JavaScript (или Мэллори)… если вы не добавите canvas в смесь, в этом случае вы сгенерируете ошибка нарушения того же происхождения).


Почему применяется та же политика происхождения, когда вы не думаете, что она должна

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

  • Алиса, Боб и Мэллори - один и тот же человек.
  • Боб предоставляет полностью публичную информацию

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


Почему одна и та же политика происхождения применяется только к JavaScript на веб-странице

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

Нет третьей стороны (Мэллори), которая считается риском.


Почему вы можете отображать данные на странице, не читая их с помощью JS

Существует ряд обстоятельств, когда сайт Мэллори может заставить браузер получать данные от третьей стороны и отображать их (например, добавляя элемент <img> для отображения изображения). Мэллори JavaScript не может прочитать данные на этом ресурсе, хотя это может сделать только браузер Алисы и сервер Боба, поэтому он по-прежнему безопасен.


CORS

Заголовок Access-Control-Allow-Origin, указанный в сообщении об ошибке, является частью стандарта CORS, который позволяет Бобу явно предоставлять разрешение сайту Мэллори на доступ к данным через браузер Алисы.

Базовая реализация будет включать в себя:

Access-Control-Allow-Origin: *

… разрешить любому веб-сайту читать данные.

Access-Control-Allow-Origin: http://example.com/

… разрешит доступ к нему только определенному сайту, и Боб может динамически сгенерировать его на основе заголовка запроса Origin, чтобы разрешить доступ к нему нескольким, но не всем сайтам.

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

Model of where CORS rules are applied

NB. Некоторые запросы являются сложными и отправляют предварительный запрос OPTIONS, на который сервер должен будет ответить, прежде чем браузер отправит запрос GET/POST/PUT/независимо от того, что JS хочет сделать. Реализации CORS, которые только добавляют Access-Control-Allow-Origin к определенным URL-адресам, часто запутываются этим.


Очевидно, что предоставление разрешения через CORS - это то, что Боб сделал бы только в том случае, если либо:

  • Данные не были частными или
  • Мэллори доверяли

Но я не Боб!

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

Если у Боба есть открытый API, то может быть механизм для включения CORS (возможно, путем форматирования запроса определенным образом или с помощью параметра конфигурации после входа на сайт портала разработчика для сайта Боба). Это должен быть механизм, реализованный Бобом. Мэллори может прочитать документацию на сайте Боба, чтобы узнать, доступно ли что-нибудь, или она могла бы поговорить с Бобом и попросить его внедрить CORS.


Сообщения об ошибках, в которых упоминается "Ответ на предпечатную проверку"

Некоторые запросы из разных источников предварительно просвечиваются.

Это происходит, когда (грубо говоря) вы пытаетесь сделать перекрестный запрос, который:

  • Включает учетные данные, такие как файлы cookie
  • Не может быть сгенерировано с помощью обычной формы HTML (например, имеет настраиваемые заголовки или тип содержимого, который нельзя использовать в форме enctype).

Если вы правильно делаете что-то, что требует предварительной проверки

В этих случаях остальная часть этого ответа по-прежнему применяется, но вам также необходимо убедиться, что сервер может прослушивать запрос предварительной проверки (который будет OPTIONS (а не GET, POST). или что-либо, что вы пытались отправить) и ответьте на него правильным заголовком Access-Control-Allow-Origin, а также Access-Control-Allow-Methods и Access-Control-Allow-Headers, чтобы разрешить ваши конкретные методы HTTP или заголовки.

Если вы по ошибке запускаете предпечатную проверку

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

Распространенные ошибки, которые вызывают это, включают:

  • попытка поставить Access-Control-Allow-Origin и другие заголовки ответа CORS на запрос. Они не относятся к запросу, не делают ничего полезного (в чем смысл системы разрешений, где вы можете предоставить себе разрешение?) И должны появляться только в ответе.
  • попытка поместить заголовок Content-Type: application/json в запрос GET, у которого нет тела запроса для описания содержимого (обычно, когда автор путает Content-Type и Accept).

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


Непрозрачные ответы

Иногда вам нужно сделать HTTP-запрос, но вам не нужно читать ответ. например если вы отправляете сообщение журнала на сервер для записи.

Если вы используете API fetch (а не XMLHttpRequest), вы можете настроить его так, чтобы он не пытался использовать CORS.

Обратите внимание, что это не позволит вам делать то, что вам требуется от CORS. Вы не сможете прочитать ответ. Вы не сможете сделать запрос, который требует предварительной проверки.

Это позволит вам сделать простой запрос, не видеть ответ и не заполнять консоль разработчика сообщениями об ошибках.

Как это сделать, объясняется в сообщении об ошибке Chrome, которое выдается, когда вы делаете запрос с помощью fetch и не получаете разрешения на просмотр ответа с помощью CORS:

Доступ к выборке в 'https://example.com/' из источника "https://example.net" был заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок "Access-Control-Allow-Origin". Если непрозрачный ответ отвечает вашим потребностям, установите режим запроса "no-cors", чтобы получить ресурс с отключенным CORS.

Таким образом:

fetch("http://example.com", { mode: "no-cors" });

Альтернативы CORS

JSONP

Боб мог также предоставить данные, используя взлом, например, JSONP, который использовался Ajax для перекрестного происхождения до появления CORS.

Он работает, представляя данные в форме JavaScript-программы, которая вводит данные на страницу Мэллори.

Это требует, чтобы Мэллори доверяла Бобу, чтобы он не предоставлял вредоносный код.

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

Поскольку JSONP работает путем добавления элемента <script> для загрузки данных в форме программы JavaScript, которая вызывает функцию, уже находящуюся на странице, попытка использовать технику JSONP для URL-адреса, возвращающего JSON, будет неудачной - обычно с ошибкой CORB - потому что JSON - это не JavaScript.

Переместите два ресурса в один источник

Если HTML-документ, в котором выполняется JS, и запрашиваемый URL-адрес находятся в одном источнике (с одной и той же схемой, именем хоста и портом), то они по умолчанию разрешают политику Same Origin Policy. CORS не нужен.

Прокси-сервер

Мэллори могла использовать серверный код для извлечения данных (которые она могла затем передать со своего сервера в браузер Алисы через HTTP, как обычно).

Это будет либо:

  • добавить заголовки CORS
  • преобразовать ответ в JSONP
  • существуют в том же источнике, что и HTML-документ

Этот код на стороне сервера может быть написан & размещенный третьей стороной (такой как CORS Anywhere). Обратите внимание на последствия этого для конфиденциальности: третья сторона может отслеживать, кто что прокси-сервер на своих серверах.

Бобу не нужно было давать никаких разрешений для этого.

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

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

Написание чего-то другого, кроме веб-приложения

Как отмечено в разделе "Почему одна и та же политика происхождения применяется только к JavaScript на веб-странице", вы можете избежать SOP, не записывая JavaScript на веб-странице.

Это не означает, что вы не можете продолжать использовать JavaScript и HTML, но вы можете распространять их, используя другой механизм, например Node-WebKit или PhoneGap.

Расширения браузера

Расширение браузера может добавить заголовки CORS в ответ до применения той же политики происхождения.

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

Они также имеют тенденцию работать только с простыми запросами (сбой при обработке предварительных запросов OPTIONS).

Наличие надлежащей среды разработки с локальным сервером разработки обычно лучший подход.


Другие угрозы безопасности

Обратите внимание, что SOP/CORS не ослабляют атаки XSS, CSRF или SQL Injection, которые необходимо обрабатывать независимо.


Резюме

  • В коде на стороне клиента вы ничего не можете сделать, чтобы обеспечить CORS-доступ к другому серверу.
  • Если вы управляете сервером, запрос делается: Добавьте к нему разрешения CORS.
  • Если вы дружите с человеком, который его контролирует: попросите их добавить в него разрешения CORS.
  • Если это государственная служба:
    • Прочитайте их документацию по API, чтобы узнать, что они говорят о доступе к нему с помощью клиентского JavaScript:
      • Они могут сказать вам, чтобы использовать определенные URL-адреса
      • Они могут поддерживать JSONP
      • Они могут вообще не поддерживать перекрестный доступ из кода на стороне клиента (это может быть преднамеренным решением по соображениям безопасности, особенно если вам необходимо передавать персонализированный ключ API в каждом запросе).
    • Убедитесь, что вы не запускаете предполётный запрос, который вам не нужен. API может предоставлять разрешения для простых запросов, но не для предварительно определенных запросов.
  • Если ничего из вышеперечисленного не применимо: заставьте браузер вместо этого общаться с вашим сервером, а затем попросите ваш сервер извлечь данные с другого сервера и передать их. (Существуют также сторонние размещенные сервисы, которые прикрепляют заголовки CORS к общедоступным ресурсам, которые вы можете использовать).

Ответ 2

Целевой сервер должен разрешать запрос с кросс-началом. Чтобы разрешить это с помощью экспресс-функции, просто выполните запрос параметров http:

app.options('/url...', function(req, res, next){
   res.header('Access-Control-Allow-Origin', "*");
   res.header('Access-Control-Allow-Methods', 'POST');
   res.header("Access-Control-Allow-Headers", "accept, content-type");
   res.header("Access-Control-Max-Age", "1728000");
   return res.sendStatus(200);
});

Ответ 3

Это происходит из-за ошибки CORS. CORS означает совместное использование ресурсов Cross Origin. Говоря простыми словами, эта ошибка возникает, когда мы пытаемся получить доступ к домену/ресурсу из другого домена.

Подробнее об этом здесь: Ошибка CORS с jquery

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

Как получить отклик для совместного использования ресурсов (CORS).

Эти ссылки могут помочь

Ответ 4

Как это не упоминается в принятом ответе.

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

Вы можете использовать простые запросы.
Чтобы выполнить "простые запросы", запрос должен соответствовать нескольким условиям. Например, разрешить только метод POST, GET и HEAD, а также разрешить только некоторые заданные заголовки (вы можете найти все условия здесь).

Если ваш клиентский код не устанавливает явные заголовки (например, "Принять") с фиксированным значением в запросе, может случиться так, что некоторые клиенты автоматически устанавливают эти заголовки с некоторыми "нестандартными" значениями, в результате чего сервер не принимает его как Простой запрос - который даст вам ошибку CORS.

Ответ 5

Эта проблема CORS не получила дальнейшего развития (по другим причинам).

У меня эта проблема в настоящее время по другой причине. Мой интерфейс также возвращает ошибку заголовка "Access-Control-Allow-Origin".

Просто я указал неправильный URL, поэтому этот заголовок не был отражен должным образом (в котором я предположил, что он это сделал). localhost (внешний интерфейс) → вызов незащищенного http (должен быть https), убедитесь, что конечная точка API от внешнего интерфейса указывает на правильный протокол.

Ответ 6

(ПРОСТОЕ РЕШЕНИЕ ДЛЯ ANGULAR) Это меня тоже сводило с ума, есть простой встроенный прокси, который вы можете установить с помощью npm → более ранний ответ здесь.

Ответ 7

Другое решение - использовать браузер Mozilla.

Ответ 8

В моем сценарии

Я получил ошибку ниже при вызове моего собственного API-интерфейса с моего внешнего интерфейса

ошибка:

Доступ к XMLHttpRequest в ' https://Localhost: 8443/например/TT/ ' от происхождения ' HTTP://локальный: 3000 ' был заблокирован политикой CORS: поле заголовка запроса ТипСодержимого не допускается Access-Control-allow- Заголовки в предполетном ответе.

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

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Headers", "contenttype");
  next();
}

Ответ 9

Вы должны включить CORS, чтобы заставить его работать.

Ответ 10

Если вы получаете эту ошибку при разработке расширения для google chrome, просто добавьте -

  "permissions": [
"http://www.google.com/"

]

в файле manifest.json.
** Замените Google своим сторонним URL.