Ответ 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-сервера Боба и/или языка программирования на стороне сервера. Существует набор руководств для различных распространенных конфигураций, которые могут помочь.
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 может предоставлять разрешения для простых запросов, но не для предварительно определенных запросов.
- Прочитайте их документацию по API, чтобы узнать, что они говорят о доступе к нему с помощью клиентского JavaScript:
- Если ничего из вышеперечисленного не применимо: заставьте браузер вместо этого общаться с вашим сервером, а затем попросите ваш сервер извлечь данные с другого сервера и передать их. (Существуют также сторонние размещенные сервисы, которые прикрепляют заголовки CORS к общедоступным ресурсам, которые вы можете использовать).