Когда я пытаюсь сделать запрос GET
, POST
, PUT
, PATCH
,DELETE
из своего внешнего интерфейса, я получаю от сервера
Запрос из другого источника заблокирован: та же политика источника запрещает чтение удаленного ресурса по адресу http://my_server/ (причина: отсутствует заголовок «Access-Control-Allow-Origin» CORS).
Я также видел это на английском языке что-то вроде:
«В запрошенном ресурсе отсутствует заголовок« Access-Control-Allow-Origin ». Следовательно, «нулевой» источник не имеет доступа».
Я изучил ЧТО такое CORS и комментарии W3C по этой теме , и я понимаю, что означает Cross-Origin Resource Sharing , но я не понимаю, как это работает.
Как я могу решить эту проблему в своих веб-приложениях?
Наконец, я заметил, что мои клиентские приложения обычно делают два запроса, один типа OPTIONS
и общие [ GET
, POST
, PUT
, PATCH
....] это потому, что
Я делаю тесты с помощью test-cors.org, и он выдает ту же ошибку.
CORS — это аббревиатура, относящаяся к правилам безопасности, которые гарантируют связь между двумя точками с другим приоритетом, чем домен, из которого он вызывается (т. е. разные домены).
Как правило, эти правила реализуются веб-клиентом, и именно он отвечает за проверку доставки информации или ее отклонение в зависимости от ответа сервера.
На показанном изображении эти правила активируются при попытке доставки/получения информации с сервера, соответствующего «another-domain.mx», если обмен не производится в соответствии с установленными на сервере, веб-клиент отменит запрос даже прежде всего.
Access-Control-Allow-Origin: это заголовок, который возвращается, чтобы указать, может ли ответ быть передан запрашивающему домену. Вы можете указать домены, с которыми вы хотите поделиться информацией (через запятую) или звездочкой (*), если вы не знаете происхождение ваших заявителей.
Примечание: звездочка обычно полезна, если у вас открытый (публичный) API, в противном случае не рекомендуется его использовать.
PHP-код:
Конфигурация в HTACCESS:
Согласно информации из MDN , эти запросы проверяются, и это тот же браузер, который выполняет этот вызов OPTIONS, чтобы подтвердить, что запрос, за которым последует, действителен.
Поток выглядит следующим образом:
Access-Control-Allow-Methods: POST, DELETE
.MDN сообщает нам следующее:
Пример
Следующий код представляет собой пример проверки, выполняемой при получении запроса OPTIONS, и он отвечает на разрешенные методы, чтобы браузер мог продолжить работу.
PHP-код:
Примечание: изображения были созданы с помощью инструмента Draw.io.
В основном это происходит, когда сервер, неизвестный вашему серверу, пытается получить к нему доступ.
Чтобы решить эту проблему, будет зависеть от вашей архитектуры. Например, если у вас есть апи-управление, вам было бы удобно включить конкретный сервер в своем заголовке ответа , либо дать полный доступ.
Теперь, если у вас его нет, это будет зависеть от сервера, который вы смонтировали. В случае с Nodejs для него есть даже библиотека: https://www.npmjs.com/package/cors
Пример узла js с Expressjs :
первое, что вы должны сделать, это установить его на свой серверный узел
После установки вы должны настроить свой основной файл, предположим, что он называется app.js.
Вы должны добавить клиентов, которые могут иметь доступ, предположим, что у вас есть внешнее приложение, работающее
localhost
на порту.3000
Ваш config.js будет выглядеть так:
Этот ответ призван объяснить, что такое CORS на человеческом языке, как он работает, показать, что это не тривиальная проблема и что к его настройке нельзя относиться легкомысленно. Все это в моем понимании.
Заранее извиняюсь, если ответ получился слишком длинным и если он затрагивает темы, которые не были включены в вопрос ОП.
По конкретным вопросам:
Если вы уже знаете, что такое CORS, вы можете прочитать непосредственно Как работает CORS? ниже.
Ответ на эту часть содержится в предполетных заказах (перед отправкой) .
Этот полный ответ касается того, что, по моему мнению, необходимо учитывать, чтобы включить CORS на сервере.
Основываясь на содержимом и типе доступа, вы должны определить, какие заголовки будут возвращены вашим сервером.
Мое личное мнение состоит в том, чтобы максимально ограничить авторизацию, явно серверами, которые вы контролируете и к которым должен быть доступ.
Я хотел бы убедиться, что способ, который я использую для управления доступом, не входит в число случаев, которые, как известно, представляют уязвимости. (Представленные здесь, вероятно, неполный список, но достаточный, чтобы дать вам представление о том, с чего начать)
И, наконец, если конфигурация не работает, я бы проверил, соответствует ли моя реализация какой-либо из распространенных конфигураций, которые работают. не работают, потому что они недействительны.
CORS (обмен ресурсами между источниками)
CORS — это стандарт для контролируемого совместного использования ресурсов между доменами.
Что такое домены?
Домен в контексте CORS — это комбинация протокола, хоста и порта, которая идентифицирует сервер, с которого мы будем запрашивать ресурсы. Например, в "
http://www.example.com:8080
" http — это протокол, www.example.com — хост, а 8080 — порт. В качестве альтернативы хост может быть IP-адресом, а при отсутствии номера порта это будет протокол по умолчанию (например, 80 для http и 443 для https).Что такое ресурсы?
Ресурсы — это содержимое, которое сервер возвращает по запросу. Это может быть html-контент, css, javascript, шрифты, изображения, xml, pdf и т. д.
Кто делает заказ?
Да пользователь. Но как? Например, с помощью браузера. Наиболее общий термин для браузера — «
User agent
» (буквальный перевод — User Agent).Существуют разные типы "
User agent
" (например, CURL - это еще один тип пользовательского агента), но нас будут интересоватьCORS
веб-браузеры (FireFox, Chrome, Edge, Safari, Opera и т. д.) из-за того, что они позволяют выполнятьjavascript
и поддерживатьXHR
(расширяемый язык разметки / протокол передачи гипертекста) и более новые файлыFetch
.Это
Apis
позволяет кодуjavascript
делать запросы ресурсов к доменам, как если бы пользователь браузера вводил URL-адрес в адресную строку .Следует отметить важное отличие: когда пользователь вводит URL-адрес в адресную строку , он делает это сознательно, и результат отображается на экране.
Когда запрос делается из javascript, именно посещаемый домен набирает запрос, но выполняет его как если бы он был пользователем, а результат получает внутри javascript в переменной.
В этом случае пользователь, вероятно, не знает, что происходит.
Где проблема во всем этом?
Проблема в том, как работает веб-браузер, и в
javascript
том, что пользователь запускает, не зная об этом.Давайте посмотрим на пример:
У пользователя есть учетная запись в веб-приложении «LosHorarioDeLaFamilia», которая находится в домене
A
(A
представляет протокол, хост и порт, как показаноB
ниже), и где все члены семьи записывают, что они делают и в какое время могут согласовывать каждый.У приложения есть имя пользователя и пароль, оно запрашивает пин-код для входа на новое устройство, оно работает через https, оно контролирует csrf, короче говоря, очень безопасно.
Пользователь входит в приложение со своим именем пользователя и паролем, и приложение предоставляет ему сеанс, который сохраняется в файле cookie в браузере.
С помощью url
A/verhorarios
настраивается расписание каждого для организации дня, а затем в браузере открывается еще одна вкладка со ссылкой на пришедшее по почте предложение.Ссылка может
B/ofertamaliciosa
бытьB
это второй домен, который отличается от того,A
где находится веб-приложение).Когда вы открываете эту ссылку, загружается экран, показывающий продукт, а также код
javascript
, который вы используетеXHR
для размещения заказа с помощьюA/verhorarios
.Заказ осуществляется из браузера пользователя.
cookie
Поскольку для домена существует одинA
(у которого есть сеанс пользователя),cookie
он отправляется в доменA
, сервер в доменеA
не видит никакой разницы между запросом, сделанным с помощью,XHR
и запросом, сделанным из адресной строки, поэтому он проверяет сессии и возвращает расписания семьи (в html).Ответ от домена сохраняется в
A
переменнойresultado
callXHR
а на следующем этапе вредоносный скрипт делает еще одинXHR
на сервер, контролируемый злоумышленником, отправляя полученный результат и тем самым умудряясь увидеть конфиденциальную информацию пользователя.И пользователь не узнал и не имел практического способа избежать этого.
Если в примере неясно, проблема вызвана браузером, позволяющим
javascript
домену выполнять запросы к другому домену от имени пользователя.Но если бы это было так, мог бы какой-либо сайт получать информацию с других сайтов, посещаемых тем же браузером?
Это правда. Но браузеры уже давно реализовали политику одного и того же источника (
Same Origin
), где они в основном предотвращаютjavascript
междоменное выполнение.Итак, еще раз, что такое CORS и для чего он нужен?
Политика
Same Origin
прямо предотвращает перекрестный вызовjavascript
, что вы много раз хотели бы сделать.Примером может быть наличие Интернета на одном порту и API на другом порту, выполнение вызовов из Интернета на API. В этом случае меняется порт.
Другим примером могут быть поддомены, которым необходимо совместно использовать ресурсы основного домена. Изменение поддомена меняет хост и считается другим доменом.
Затем браузеры, реализующие стандарт,
CORS
позволяют ослабить политикуSame Origin
, чтобы браузер мог проверить домен, предоставляющий ресурс, можно ли запросить его из другого другого домена, который мы будем называтьOrigin
(происхождение).Таким образом, браузер (ответственный за проблему) может определить, разрешить ли вызов и/или доставить полученный контент.
Как работает КОРС?
Браузер реализует
CORS
это через серию заголовков, которыми обмениваются с сервером.Когда браузер обнаруживает кросс-оригинальный заказ, он добавляет к остальным заголовкам заказа один
Origin
с доменом, сscript
которого был загружен инициатор заказа.С другой стороны, тот
script
, кто делает запрос, может указать, включает онcredenciales
или нет (по умолчанию не отправляются учетные данные).В этом контексте
credenciales
этоcookies
,la cabecera Authorization
(который используется как при базовой аутентификации, так и при аутентификации носителя/токена), аCertificados de cliente
также используются для аутентификации.Ожидаемый ответ от сервера будет примерно таким: Поскольку этот запрос
involucra
/no involucra
отправка учетных данных и инициированX
,se puede
/no se puede
перенос).Этот ответ материализуется одним из следующих способов:
а) Запрос без учетных данных
Если запрос сделан без
credenciales
сервера, вы можете отправить свой ответ:Эй. Без добавления какого-либо заголовка, который браузер интерпретирует как отказ в доступе. Обратите внимание, что это может быть связано с явным запретом или тем, что сервер не реализует CORS и игнорирует заголовки.
II. Добавление заголовка
Access-Control-Allow-Origin:*
, позволяющего любому домену получить доступ к ответу.III. Добавление заголовка
Access-Control-Allow-Origin: dominio
, в котором указывается тот , кdominio
которому есть доступ.б) Запрос с учетными данными
. Если запрос содержит
credenciales
, для предоставления доступа к ресурсу помимо заголовкаAccess-Control-Allow-Origin
необходимо добавить еще один заголовокAccess-Control-Allow-Credentials:true
.А в шапке
Access-Control-Allow-Origin
обязательно указать домен, к которому вы даете доступ (нельзя использовать подстановочный знак*
).Единственное значение, которое он может принимать ,
Access-Control-Allow-Credentials
этоtrue
, способ отрицания заголовка состоит в том, чтобы не включать его в ответ.Заголовок варьируется: Происхождение
Если существует более одного домена, который может быть авторизован, то домен, возвращаемый сервером, создается динамически и потенциально меняется от заказа к заказу на основе полученного заголовка
Origin
.В этом случае заголовок должен быть добавлен к ответам от сервера
Vary:Origin
, чтобы браузер не кэшировал ответы. Этот заголовок также может предотвратить уязвимость браузера «отравление кэша».Предполетные заказы (перед отгрузкой)
Браузеры могут (потому что не все это делают) отправить на сервер предварительный запрос о том, какие методы и заголовки разрешены для данного источника.
Для этого браузер отправляет запрос
OPTIONS
с заголовкамиOrigin
,Access-Control-Request-Method
, и, если применимо, такжеAccess-Control-Request-Headers
.Это должно происходить при возникновении одного из следующих условий:
1) Метод заказа ни
GET
ни ниHEAD
.2) Метод заказа
POST
и заголовок отличенContent-Type
отapplication/x-www-form-urlencoded
,multipart/form-data
илиtext/plain
.3) Вручную добавьте заголовки, отличные от:
Accept
,Accept-Language
,Content-Language
илиContent-Type
.Сервер, если он примет запрос для этого источника, отвечает на предварительную проверку следующим образом:
Access-Control-Allow-Origin: dominio
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
(Поддерживаемые методы)Access-Control-Max-Age: <tiempo-en-segundos>
(Время, в течение которого ответ остается действительным)К этим заголовкам будут добавлены:
Access-Control-Allow-Headers: <lista de cabeceras>
(включенные заголовки разделены запятыми), если были запрошены поддерживаемые заголовки.И/или
Access-Control-Allow-Credentials:true
, если запрос будет разрешен с учетными данными.Что следует помнить при настройке CORS на сервере
Конфигурации CORS не работают (конфигурации относятся к тому, что сервер возвращает в ответ)
Следующие конфигурации CORS часто встречаются в Интернете, но они являются недопустимыми конфигурациями.
1) Разрешить списки доменов, разделенные пробелами
=> Допустимые значения: *, один домен или ноль (обратите внимание, что ноль — это уязвимость, см. ниже)
2) Включить все домены с учетными данными
=> Como esta configuración claramente permite a cualquier sitio ejecutar un pedido con credenciales, es una configuración inválida que será rechazada por los navegadores.
3) Comodines (
*
) como parte de un dominio.=> La intención sería darle acceso a cualquier subdominio de example.com, pero el comodín no puede usarse de esta forma.
Problemas conocidos de seguridad en configuración de CORS
Esta lista tiene el propósito de mostrar vulnerabilidades que puede generar una mala configuración de CORS en el servidor.
No pretende ser una lista exhaustiva sino ejemplos de lo que puede pasar si la configuración de CORS se toma como algo trivial.
1) Saltar el control
Access-Control-Allow-Origin:*
concredenciales
, copiando elOrigin
del pedido alAccess-Control-Allow-Origin
de la respuesta concredenciales
.Probablemente sea el producto de encontrar una solución cuando la alternativa de permitir todos los orígenes ('
*
') con credenciales no funciona. El problema es que de esta forma se desactiva completamente la política deSame Origin
.Relacionado a este caso vale la pena mencionar el hacer una verificación ineficiente antes de copiar el domino recibido en la cabecera
Origin
:1.1 Verificar que el dominio comience on un patrón determinado.
=> Verificar que comience con:
https://ejemplo.com
autorizaría acceso ahttps://ejemplo.com.otrodominio.com
por ejemplo.1.2 Verficiar que el dominio termine con un patrón determinado.
=> Verificar que termine en
ejemplo.com
autorizaría acceso ahttps://otroejemplo.com
1.3 Expresiones regulares.
=> El problema con la expresión regular es que si aparte de reconocer el patrón del dominio o subdominio, da lugar a reconocer algo más, ese algo más podría usarse para engañar al servidor.
1.4 Ya se mencionó antes: No incluir la cabecera
Vary:Origin
=> Si el dominio indicado con
Access-Control-Allow-Origin
se genera en forma dinámica dependiendo de la cabeceraOrigin
, siempre debe devolvese la cabeceraVary:Origin
para evitar que se cache el resultado.2) Devolver la cabecera
Access-Control-Allow-Origin: null
En un pedido de origen cruzado, el navegador colocará como
Origin
, el dominio deljavascript
que está ejecutando el pedido, pero este, haciendo un manejo conveniente de redirecciones, puede provocar que al servidor le llegue elOrigin:null
.Si el servidor responde (equivocadamente)
Access-Control-Allow-Origin: null
para indicar que no da acceso, el navegador interpretará que SÍ está dando acceso al pedido.3) Anular la conexión segura sobre
https
Si los pedidos a un dominio de confianza son sobre
https
entonces debe validarse que el protocolo autorizado también sea https (no http).4) Utilizar
Access-Control-Allow-Origin:*
(sincredenciales
)Devolviendo esta cabecera, el código javascript no podrá hacer pedidos que requieran credenciales, pero todavía quedan pueden darse los siguientes escenarios:
4.1 Utilizar el navegador como proxy para hacer pedidos a la intranet del usuario.
=> Si el usuario estuviera corriendo un servidor en el puerto 8080 localmente y devolviendo esta cabecera, el sitio remoto podría ejecutar pedidos como http://127.0.0.1:8080, algo que en principio parece imposible.
Lo mismo si el usuario tuviera acceso a una intranet (sin
credenciales
).=> También permite aprovechar potenciales amenazas producto de otras fallas de configuración.
Ссылки:
https://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS
https://portswigger.net/blog/exploiting-cors-misconfigurations-for-bitcoins-and-bounties
Как вам указывают, это проблема в конфигурации вашего сервера, где у вас есть свой API. На этом сайте есть список решений для разных веб-серверов (Apache, IIS, Nginix, Express.js и т. д.).
В качестве примера я даю вам решение для сервера, на котором запущено приложение node.js, использующее экспресс.
Простое использование следующего работает без проблем между двумя серверами.
А в index.js (из бэкенда) следующее:
Что касается дополнений, они обычно «добавляются» при отправке запросов, например заголовки, которые вам понадобятся для вашего CORS.
Используете ли вы какие-либо фреймворки, такие как Angular, или библиотеки, такие как React/Vue?
У меня есть пояснительная ссылка об этом по адресу: https://code.i-harness.com/es/q/1cda4c5 .
Чтобы решить проблему с
NodeJS
иExpress
я использую этот код в файле конфигурации app.js, который я определил ранее.С таким промежуточным ПО: