Ошибка совместного использования ресурсов между разными источниками

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

Что такое ошибка CORS?

Допустим, вы находитесь на веб-сайте https://my-own-domain.com или, другими словами, это источник веб-сайта. С этого веб-сайта, если вы попытаетесь получить доступ к ресурсу из какого-либо другого источника, такого как https://some-another-domain.com, браузер блокирует запрос и показывает ошибку в консоли.

Почему браузер блокирует кросс-оригинальный запрос?

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

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

Например, вы можете войти в приложение example-application.com, и домен сохранит файл cookie сеанса. Это полезно, так как вам не нужно снова входить в систему, и домен может идентифицировать вас. Теперь представьте себе сценарий, в котором вы нажали на хитрую рекламу, и она открыла другой домен, malware-application.com. Этот новый домен может сделать запрос к example-application.com из вашего браузера, и ваши файлы cookie сеанса будут автоматически прикреплены к запросу. example-application.com идентифицирует файл cookie и позволит вредоносному приложению получить доступ к ресурсу. Это известно как атака с подделкой межсайтовых запросов.

Чтобы избежать такого сценария, браузер следует политике того же источника и блокирует выполнение запроса вредоносным приложением.

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

1. Разрешить CORS на сервере

Рассмотрим сценарий, в котором у вас есть два разных домена my-application.com и my-apis.com. Вам нужно вызвать API в my-apis.com из my-application.com. Браузер заблокирует запрос из-за политики того же источника.

Чтобы обойти это, вы можете разрешить запросы из разных источников на my-apis.com.

Этот доступ можно выборочно предоставлять некоторым доменам или использовать подстановочный знак для разрешения запросов из любого домена. Вы можете ограничить метод запросов, таких как GET/POST и т. д.

Ниже приведен пример кода для разрешения обмена запросами между источниками (CORS), написанный на экспресс-сервере:

// Add headers
app.use(function (req, res, next) {    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'https://my-application.com');    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);    // Pass to next layer of middleware
    next();
});

2. Используйте прокси

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

2.1 Создание промежуточного ПО

В примере, объясненном ранее, вы можете иметь API промежуточного слоя на сервере, отображающем my-application.com. Вместо того, чтобы вызывать ресурс в my-apis.com непосредственно из браузера, вызовите API промежуточного слоя в my-application.com и позвольте API промежуточного слоя сделать запрос к my-apis.com.

2.2 Пусть ваш веб-сервер сделает всю работу

Вы можете использовать свой веб-сервер (Nginx, Apache и т. д.) в качестве промежуточного программного обеспечения и прокси-сервера для передачи запросов.

Например, добавление нового маршрута в Nginx и прокси, передающего запросы по этому маршруту на my-apis.com, позволяет my-application.com выполнять вызов по указанному маршруту.

местоположение /api {
proxy_pass https://my-apis.com/;
}

Вывод

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

Понимание ошибки CORS и решений помогает нам лучше понять, как работают веб-системы.

I have a domain hosted in Godaddy. but need to do mail configuration through AWS lambda for static website. getting error «Cross Origin Resource Sharing Error» when submit enquiry form.

Done CORS configuration in lambda code and API GW also (attached screenshot)

This is lambda code

var AWS = require('aws-sdk');
var ses = new AWS.SES();
 
var RECEIVER1 = 'yyyyy@gmail.com';
var RECEIVER2 = 'xxxxx@gmail.com';
var SENDER = 'xxxxx@gmail.com';

var response = {
 "isBase64Encoded": false,
 "headers": { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': ''},
 "statusCode": 200,
 **headers: {
            "Access-Control-Allow-Headers" : "Content-Type",
            "Access-Control-Allow-Origin": "",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },**
 "body": "{"result": "Success."}"
 };

exports.handler = (event, context, callback) => {
    callback(null, { statusCode: 200, body: 'Hello from Lambda' });

};
 
function sendEmail (event, done) {
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER1,RECEIVER2
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: 'name: ' + event.name +'\nlastname: ' + event.lastname+ '\nphone: ' + event.phone+ '\nemail: ' + event.email+ '\nAddress: ' + event.address,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: 'Call Back Message from User ',
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
}"

[enter image description here](https://i.stack.imgur.com/SyLJl.jpg)

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

Если вы давно хотели разобраться в CORS и вас достали постоянные ошибки, добро пожаловать под кат.

Ошибка в консоли вашего браузера

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://example.com/

Access to fetch at ‘https://example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.

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

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

Но давайте-ка пойдем к истокам…

В начале был первый субресурс 

Субресурс  — это HTML элемент, который требуется вставить в документ или выполнить  в контексте этого документа. В 1993 году был введен первый тег <img>. С появлением веб стал более красивым, но заодно и стал сложнее.


Верни мне мой 1993 г.

Как вы поняли, если ваш браузер отображает страницу с <img>, он должен запросить этот тег из источника. Если браузер запрашивает тег из источника, который отличается от получателя по схеме, в полностью определенному имени хоста или порту, то это и есть запрос между различными источниками (cross-origin request).

Источники & cross-origin

Источник идентифицируется следующей тройкой параметров: схема, полностью определенное имя хоста и порт. Например, <http://example.com> и <https://example.com> имеют разные источники: первый использует схему http, а второй https. Вдобавок, портом для http по умолчанию является 80, тогда как для https — 443. Следовательно, в данном примере 2 источника отличаются схемой и портом, тогда как хост один и тот же (example.com).

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

Если, к примеру, мы будем сравнивать источник <https://blog.example.com/posts/foo.html> с другими источниками, то мы получим следующие результаты:

Пример запроса между различными источниками: когда ресурс (то есть, страница) типа <http://example.com/posts/bar.html> попробует отобразить тег из источника <https://example.com> (заметьте, что схема поменялась!).

Слишком много опасностей запроса между различными источниками

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

Когда тег <img> появился во Всемирной Паутине, мы тем самым открыли ящик Пандоры. Некоторое время спустя в Сети появились теги <script>, <frame>, <video>, <audio>, <iframe>, <link>, <form> и так далее. Эти теги могут быть загружены браузером уже после загрузки страницы, поэтому они все могут быть запросами в пределах одного источника и между о разными источниками. 

Давайте погрузимся в воображаемый мир, где не существует CORS и веб-браузеры допускают все типы запросов между источниками. 

Предположим, у меня есть страница на сайте evil.com с <script>. На первый взгляд это обычная страница, где можно прочесть полезную информацию. Но я специально создал код в теге <script>, который будет отправлять специально созданный запрос по удалению аккаунта (DELETE/account) на сайт банка. Как только вы загрузили страницу, JavaScript запускается и AJAX-запрос попадает в  API банка. 


Вжух, нет вашего аккаунта

Немыслимо, да? Представьте, что вы читаете что-то на веб странице и вам приходит электронное письмо от банка, что ваш аккаунт успешно удален. Знаю, знаю… если бы было так просто провести любую банковскую операцию… Отвлекся.

Для того чтобы мой вредоносный <script> сработал, ваш браузер должен был также отправить ваши учетные данные (куки), взятые с банковского сайта, как часть запроса. Именно таким образом банковские серверы идентифицировали бы вас и знали, какой аккаунт нужно удалить.

Давайте рассмотрим другой, не такой зловещий сценарий.

Мне нужно опознать людей, которые работают на Awesome Corp, внутренний сайт этой компании  находится по адресу intra.awesome-corp.com. На моем сайте, dangerous.com, у меня есть <img src="https://intra.awesome-corp.com/avatars/john-doe.png">.

У пользователей, у которых нет активного сеанса с intra.awesome-corp.com, аватарка не отобразится, так как это приведет к ошибке. Однако если вы совершили вход во внутреннюю сеть Awesome Corp., как только вы откроете мой dangerous.com сайт, я буду знать, что у вас там есть аккаунт. 

Это означает, что я смогу прощупать  определенную информацию о вас. Конечно, для меня будет сложнее устроить атаку, но знание о том, что у вас есть доступ к Awesome Corp., является потенциальным направлением  для атаки.


Утечка информации к 3-им лицам

Эти два примера крайне упрощены, но именно такие угрозы обусловили необходимость политики одинакового источника и CORS… Существуют разнообразные опасности, связанные с запросами между разными источниками. Некоторые из них можно сгладить, другие нет: они укоренены в природе интернета. Однако огромное количество заблокированных атак — это заслуга CORS.

Но до зарождения CORS существовала политика одинакового источника.

Политика одинакового источника

Политика одинакового источника предотвращает cross-origin атаки, блокируя доступ для прочтения загружаемых ресурсов из другого источника. Такая политика все еще разрешает нескольким тегам вроде <img> загружать ресурсы из других источников. 

Политика одинакового источника введена Netscape Navigator 2.02 в 1995 году, изначально для защищенного cross-origin доступа к Объектной модели документа (DOM).

Даже несмотря на то, что внедрение политики одинакового источника не требует придерживаться определенного порядка действий, все современные браузеры следуют этой политике в той или иной форме. Принципы политики описаны в запросе на спецификацию RFC6454 Инженерного совета интернета (Internet Engineering Task Force).

Выполнение политики одинакового источника определено этим сводом правил:

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

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

Врываемся в CORS

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

Давайте разберемся с совместным использованием ресурсов различными источниками (CORS). CORS — это механизм, который дает контролировать доступ к тегам на веб странице по сети. Механизм классифицируется на три разные категории доступа тегов:

  1. Запись из разных источников
  2. Вставка из разных источников 
  3. Считывание из разных источников 

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

Запись из разных источников  — это ссылки, переадресации и отправка форм. С активным CORS в вашем браузере все эти операции разрешены. Существует также штука под названием предварительный запрос, которая настраивает запись из разных источников. Таким образом, если некоторые записи могут быть разрешены, это не означают, что они будут выполнены на практике. Мы вернемся к этому немного позже. 

Вставки из разных источников — это теги, загружаемые через <script>, <link>, <img>, <video>, <audio>, <object>, <embed>, <iframe> и т.п. Все они разрешены по умолчанию. <iframe> выделяется на их фоне, так как он используется для загрузки другой страницы внутри фрейма. Его обрамление в зависимости от источника может регулироваться посредством использования заголовка  X-Frame-options.

Что касается <img> и других вставных тегов, то они устроены так, что сами инициируют запросы из разных источников cross-origin запроса. Именно поэтому в CORS существует различие между вставкой из разных источников и считыванием из разных источников. 

Считывание из разных источников  — это теги, загружаемые через вызовы AJAX/ fetch. Все они по умолчанию заблокированы вашим браузером. Существует обходной путь для вставки таких тегов на странице, но такие трюки регулируются другой политикой, которая соблюдается в современных браузерах. 

Если ваш браузер обновлён, то он уже дополнен всей этой эвристикой. 

Запись из разных источников

Операции записи из разных источников порой очень проблематичны. Давайте рассмотрим пример и посмотрим на CORS в деле.

Во-первых, у нас будет простой Crystal (с использованием Kemal) HTTP сервер:

require "kemal"

port = ENV["PORT"].to_i || 4000

get "/" do
  "Hello world!"
end

get "/greet" do
  "Hey!"
end

post "/greet" do |env|
  name = env.params.json["name"].as(String)
  "Hello, #{name}!"
end

Kemal.config.port = port
Kemal.run

Он просто берет запрос по ссылке /greet с name в теле запроса и возвращает Hello #{name}!. Чтобы запустить это маленький Crystal сервер мы можем написать

$ crystal run server.cr

 Так запускается сервер, который будет слушать localhost:4000. Если мы откроем localhost:4000 в нашем браузере, то появится страница с тривиальным «Hello World». 


Hello world!

Теперь, когда мы знаем, что наш сервер работает, давайте из консоли нашего браузера сделаем запрос  POST /greet  на сервер, слушающий localhost:4000,. Мы можем это сделать, используя fetch:

fetch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Ilija'})
  }
).then(resp => resp.text()).then(console.log)

Как только мы его запустили, мы увидим, что приветствие вернется из сервера:


Привет!

Это был POST запрос, но не из разных источников. Мы отправили запрос из браузера, где была отображена страница с адреса http://localhost:4000 (источник), к тому же источнику.

Теперь, давайте попробуем повторить такой же  запрос но с различными источниками. Мы откроем https://google.com и попробуем тот же запрос с той же вкладки нашего браузера:


Привет, CORS!

Мы смогли добраться до знаменитой ошибки CORS. Несмотря на то что наш Crystal сервер смог совершить запрос, наш браузер защищает нас от нас самих. Он говорит нам, что сайт, который мы открыли, хочет внести изменения на другом сайте.

В первом примере, где мы отправили запрос в http://localhost:4000/greet из вкладки, которая отображала http://localhost:4000, наш браузер смотрит на запрос и разрешает, так как ему кажется, что наш сайт запрашивает наш сервер (что есть отлично). Но во втором примере где наш сайт (https://google.com) хочет написать на http://localhost:4000, тогда наш браузер отмечает этот запрос и не разрешает ему пройти. 

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

Если поподробнее разобраться в консоли разработчика, в частности, заглянуть во вкладку Network, то на самом деле мы увидим два запроса вместо одного, что мы отправили:


Как видно в панеле Network, отправленных запроса две штуки

Интересно заметить, то у первого запроса в HTTP фигурирует метод OPTIONS, в то время как у второго – метод POST.

Если внимательно посмотреть на запрос OPTIONS, то мы увидим, что этот запрос отправлен нашим браузером до отправления запроса POST.


Смотрим запрос OPTIONS

Интересно, что несмотря на то, что статус запроса OPTIONS был HTTP 200, он был все же отмечен красным в списке запросов. Почему?

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

  • Запрос использует методы отличные от GET, POST, или HEAD
  • Запрос включает заголовки отличные от Accept, Accept-Language или Content-Language
  • Запрос имеет значение заголовка Content-Type отличное от application/x-www-form-urlencoded, multipart/form-data, или text/plain.

Следовательно, в примере выше, несмотря на то, что мы отправили запрос POST, браузер считает наш запрос сложным из-за заголовка Content-Type: application/json.

Если бы мы изменили наш сервер так, чтобы он обрабатывал контент text/plain (вместо JSON), мы бы могли обойтись без предварительных запросов:

require "kemal"

get "/" do
  "Hello world!"
end

get "/greet" do
  "Hey!"
end

post "/greet" do |env|
  body = env.request.body

  name = "there"
  name = body.gets.as(String) if !body.nil?

  "Hello, #{name}!"
end

Kemal.config.port = 4000
Kemal.run

Теперь, когда мы можем отправить наш запрос с заголовком Content-type: text/plain:

fetch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain'
    },
    body: 'Ilija'
  }
)
.then(resp => resp.text())
.then(console.log)

Теперь, пока предварительный запрос не будет отправлен, CORS политика браузера будет постоянно блокировать запрос:


CORS стоит насмерть

Но так как мы создали запрос, который не классифицируется как сложный, наш браузер не заблокирует запрос.


Запрос прошел

Проще говоря, наш сервер неправильно настроен на принятие text/plain запросов из разных источников без какой-либо защиты и наш браузер не сможет ничего с этим поделать. Но все же он делает следующую вещь: он не показывает нашу открытую страницу/вкладку в ответ на это запрос. Следовательно, в этом случае CORS не блокирует запрос, он блокирует ответ.

CORS политика вашего браузера считает, что это фактически считывание из разных источников, так как, несмотря на то, что запрос был отправлен как POST, Content-type значение заголовка по сути приравнивает его к GET. Считывания из разных источников заблокированы по умолчанию, следовательно мы видим заблокированный запрос в нашей панели Network.

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

Выполняя запрос OPTIONS, вы должны помнить, что предварительный запрос браузера проверяет наличие трех заголовков, которые могут быть в ответе:

  • Access-Control-Allow-Methods, который указывает на то, какие методы поддерживаются URL-ом ответа в контексте CORS протокола. 
  • Access-Control-Allow-Headers, который указывает, на то, какие заголовки поддерживаются URL-ом ответа в контексте CORS протокола.
  • Access-Control-Max-Age, который указывает число секунд (5 по умолчанию) и это значение соответствует периоду, на который предоставляемая заголовками Access-Control-Allow-Methods и Access-Control-Allow-Headers информация может быть кэширована.

Давайте вернемся к предыдущему примеру, где мы отправили сложный запрос:

etch(
  'http://localhost:4000/greet',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Ilija'})
  }
).then(resp => resp.text()).then(console.log

Мы уже выяснили, что когда мы отправляем запрос, наш браузер будет сверяться с сервером, можно ли выполнить  запрос с данными из разных источников. Чтобы обеспечить работоспособность в среде с разными источниками, мы должны сначала добавить конечную точку OPTIONS/greet к нашему серверу. В заголовке ответа новая конечная точка  должна сообщить браузеру, что запрос на POST /greet с заголовком Content-type: application/json из источника https://www.google.com может быть принят. 

Мы это сделаем, используя заголовки Access-Control-Allow-*:

options "/greet" do |env|
  # Allow `POST /greet`...
  env.response.headers["Access-Control-Allow-Methods"] = "POST"
  # ...with `Content-type` header in the request...
  env.response.headers["Access-Control-Allow-Headers"] = "Content-type"
  # ...from https://www.google.com origin.
  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
end

Если мы запустим сервер и отправим запрос, то 


Все еще заблокирован?

Наш запрос остается заблокированным. Даже несмотря на то что наша конечная точка OPTIONS/greet в самом деле разрешила запрос, мы пока еще видим сообщение об ошибке. В нашей панели Network происходит кое-что интересное:


OPTIONS стал зеленым!

Запрос в конечную точку OPTIONS/greet прошел успешно! Однако запрос POST /greet все еще терпит неудачу. Если взглянуть на внутрь запроса POST /greet мы увидим знакомую картинку:


POST тоже стал зеленым?

На самом деле запрос удался: сервер вернул HTTP 200. Предварительный запрос заработал. Браузер совершил POST-запрос вместо того, чтобы его заблокировать. Однако ответ на запрос POST не содержит никаких CORS заголовков, так что даже несмотря на то, что браузер сделал запрос, он заблокировал любой ответ. 

Чтобы разрешить браузеру обработать ответ из запроса POST /greet, нам также нужно добавить заголовок CORS к конечной точке POST:

post "/greet" do |env|
  name = env.params.json["name"].as(String)

  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"

  "Hello, #{name}!"
end

Добавляя к заголовку Access-Control-Allow-Origin заголовок ответа, мы сообщаем браузеру, что вкладка с открытой https://www.google.com имеет доступ к содержимому ответа.

Если попытаться еще разок, то 


POST работает!

Мы увидим, что POST /greet получил для нас ответ без каких-либо ошибок. Если посмотреть на панели Network, то мы увидим, что оба запроса зеленые.


OPTIONS & POST в деле!

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

Считывание из разных источников 

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

Скажем, в нашем Crystal сервере есть действие GET /greet.

get "/greet" do
  "Hey!"
end

Из нашей вкладки, что передала www.google.com если мы попробуем запросить эндпоинт GET /greet, то CORS нас заблокирует:


CORS блокирует

Если посмотрим поглубже в запрос, то мы найдем кое-что интересное:

На самом деле, как и прежде, наш браузер разрешил запрос: мы получили код состояния HTTP 200. Однако он не показал нашу открытую страницу/вкладку в ответ на этот запрос. Еще раз, в данном случае CORS не заблокировал запрос, он заблокировал ответ.

Так же, как и в случае с записью из разных источников, мы можем освободить CORS и обеспечить  считывание  из разных источников, добавляя заголовок Access-Control-Allow-Origin:

get "/greet" do |env|
  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
  "Hey!"
end

Когда браузер получит ответ от сервера, он проверит заголовок Access-Control-Allow-Origin и исходя из его значения решит, сможет ли он позволить странице прочитать ответ. Учитывая, что в данном случае значением является https://www.google.com, итог будет успешным:


Успешный запрос GET между разными источниками

Вот как наш браузер защищает нас от считывания из разных источников  и соблюдает директивы server, сообщенные через заголовки.

Тонкая настройка CORS

Как мы уже видели в предыдущих примерах, чтобы смягчить политику CORS нашего сайта мы можем присвоить опцию Access-Control-Allow-Origin для нашего действия /greet значению https://www.google.com:

post "/greet" do |env|
  body = env.request.body

  name = "there"
  name = body.gets.as(String) if !body.nil?

  env.response.headers["Access-Control-Allow-Origin"] = "https://www.google.com"
  "Hello, #{name}!"
end

Это разрешит нашему источнику https://www.google.com запросить наш сервер, и наш браузер свободно сделает это. Имея Access-Control-Allow-Origin мы можем попробовать снова выполнить вызов fetch:


Сработало!

И это работает! С новой политикой CORS мы можем вызвать действие /greet из нашей вкладки, в которой загружена страница https://www.google.com. Или, мы могли бы присвоить заголовку значение *, которое сообщило бы браузеру, что сервер может быть вызван из любого источника. 

Устанавливая такую конфигурацию, нужно тщательно взвесить все риски. Тем не менее, вставка заголовков с нестрогими требованиями к CORS почти всегда безопасна. Есть эмпирическое правило:  если вы открываете URL в приватной вкладке и вас устраивает информация, которая там отображается, то вы можете установить разрешающую CORS политику (*) для  данного URL.

Другой способ настройки CORS на нашем сайте — это использование заголовка запроса Access-Control-Allow-Credentials. Access-Control-Allow-Credentials запрашивает браузер, показывать ли ответ JavaScript  коду клиентской части, когда в качестве режима учетных данных запроса используется  include.

Учетный режим запросов исходит из внедрения Fetch API, который в свою очередь корнями идет к объектам XMLHttpRequest:

var client = new XMLHttpRequest()
client.open("GET", "./")
client.withCredentials = true

С вводом fetch, метод withCredentials превратился в опциональный аргумент fetch запроса:

fetch("./", { credentials: "include" }).then(/* ... */)

Доступными опциями для обработки учетных данных являются omit, same-origin и include. Доступны разные режимы, так что программист может настроить отправляемый запрос, пока ответ от сервера сообщает браузеру как вести себя, когда учетные  данные отправлены с запросом (через заголовок Access-Control-Allow-Credential).

Спецификация  Fetch API содержит подробно расписанный и детально разобранный функционал  взаимодействия CORS и Web API fetch, а также характеризует механизмы безопасности, используемые браузерами.

Несколько правильных решений

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

Свободный доступ для всех 

Как правило, это тот случай, когда у вас есть сайт с открытым контентом, не ограниченный платным доступом или сайт, требующий аутентификацию или авторизацию. Тогда вы должны установить Access-Control-Allow-Origin: * для ресурсов сайта.

Значение * хорошо подойдет в случаях, когда

  • Не требуется ни аутентификация, ни авторизация
  • Ресурс доступен широкой аудитории пользователей без ограничений
  • Источников и клиентов, у которых будет доступ к ресурсам великое множество, и вы не знаете о них или вам все равно, кто они.

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


Сверхупрощение VPN 

Теперь, когда взломщик захостит dangerous.com, который содержит ссылку файла с VPN, то (в теории) он может создать скрипт в их сайте, который сможет иметь доступ к этому файлу:


Утечка файла

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

Всё в семью

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

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

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

В данных случаях мы хотим, чтобы наш API установил заголовок Access-Control-Allow-Origin к URL нашего сайта. Это обеспечит нас тем, что браузеры никогда не отправят запросы нашему API с других страниц.

Если пользователи или другие сайты попробуют взломать данные нашего аналитического  API, то набор заголовков Access-Control-Allow-Origin, установленный на нашем API, не пропустит  запрос.

Null источник 

Другим интересным случаем является null источник. Это происходит, когда ресурс получает доступ от браузера, который отображает локальный файл. Например, запросы, исходящие из определенного JavaScript, работающего в статическом файле на вашем ПК, имеют заголовок Origin со значением null

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

Пропускай куки, если возможно

Как мы уже видели с Access-Control-Allow-Credentials, куки не включены по умолчанию. Чтобы разрешить отправку куки с разных источников, нужно просто вернуть Access-Control-Allow-Credentials: true. Этот заголовок сообщит браузерам, что им разрешается пересылать удостоверяющие данные (то есть куки) в запросах между разными источниками.

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

Куки между разными источниками полезнее всего в ситуациях, когда вы точно знаете какие именно клиенты будут иметь доступ к вашему серверу. Именно поэтому семантика CORS не позволяет нам установить Access-Control-Allow-Origin: *, когда удостоверяющие данные между разными источниками разрешены.

В то время как комбинация из Access-Control-Allow-Origin: * и Access-Control-Allow-Credentials: true технически разрешается, она является анти-паттерном и ее следует безусловно избегать.

Если вы хотите, чтобы к вашим серверам имели доступ разные клиенты и источники, то вам стоит рассмотреть возможность создания API с аутентификацией через пароль вместо использования куков. Но если вариант с API не является оптимальным, то обеспечьте себя защитой от фальсификатора межсайтовых запросов (CSRF).

Дополнительная литература

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

  • Cross-Origin Resource Sharing (CORS)
  • Access-Control-Allow-Credentials header on MDN Web Docs
  • Authoritative guide to CORS (Cross-Origin Resource Sharing) for REST APIs
  • The «CORS protocol» section of the Fetch API spec
  • Same-origin policy on MDN Web Docs
  • Quentin’s great summary of CORS on StackOverflow

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

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Совместное использование ресурсов между источниками CORS (совместное использование ресурсов между источниками)

1. Введение

CORS требует поддержки как браузера, так и сервера. В настоящее время ** браузер IE не может быть ниже IE10. ** Все остальные браузеры поддерживают эту функцию. Весь процесс связи CORS автоматически завершается браузером и не требует участия пользователя. Для разработчиков нет разницы между связью CORS и связью AJAX одного и того же происхождения, и код совершенно одинаковый. Как только браузер обнаружит, что запрос AJAX является кросс-источником, он автоматически добавит некоторую дополнительную информацию заголовка, а иногда будет сделан дополнительный запрос, но пользователь этого не почувствует. Следовательно, ключом к реализации связи CORS является сервер. Пока сервер реализует интерфейс CORS, он может обмениваться данными между источниками.

Два, два запроса

Браузеры делят запросы CORS на две категории: простой запрос и непростой запрос.

  • Пока следующие два условия выполняются одновременно, это простой запрос.

(1) Метод запроса — один из следующих трех методов: HEAD GET POST (2) Значение заголовка запроса Content-Type в заголовке запроса является одним из следующих application / x-www-form-urlencoded, multipart / form-data, text / plain (3) Два вышеуказанных условия выполнены, и пользовательский заголовок запроса не установлен или прослушиватель событий не добавлен в xhr.upload

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

Три, простой запрос

3.1 Основной процесс

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

Host:media.meili-inc.com
Origin:http://newsky.meili-inc.com
Скопировать код

Если источник, указанный Origin, находится за пределами разрешенной области, сервер вернет нормальный ответ HTTP. Браузер обнаружил, что информация заголовка этого ответа не содержит поля Access-Control-Allow-Origin, и знал, что произошла ошибка, поэтому была выдана ошибка, которая была захвачена функцией обратного вызова onerror XMLHttpRequest.

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

Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localcube.mogujie.org
Access-Control-Expose-Headers:Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Content-Type:application/json;charset=UTF-8
Скопировать код

В приведенной выше информации заголовка есть три поля, относящиеся к запросам CORS, все из которых открываются с помощью Access-Control- (1).Access-Control-Allow-Origin Это поле обязательно для заполнения. Его значение — это либо значение поля Origin, когда запрос сделан, либо *, что означает, что запросы для любого доменного имени принимаются. (2)Access-Control-Allow-Credentials Это поле необязательно. Его значение — логическое значение, которое указывает, разрешить ли отправку файлов cookie. По умолчанию файлы cookie не включаются в запросы CORS. Если установлено значение true, это означает, что сервер явно разрешает включение файла cookie в запрос и отправку на сервер. Это значение может иметь только значение true. Если сервер не хочет, чтобы браузер отправлял файлы cookie, просто удалите это поле. (3 **) Access-Control-Expose-Headers ** Это поле является необязательным. При запросе CORS метод getResponseHeader () объекта XMLHttpRequest может получить только 6 основных полей: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. Если вы хотите получить другие поля, вы должны указать их в Access-Control-Expose-Headers. В приведенном выше примере указано, что getResponseHeader (‘Access-Control-Allow-Origin’) может возвращать значение поля Access-Control-Allow-Origin.

3.2 атрибут withCredentials

Как упоминалось выше, запросы CORS по умолчанию не отправляют файлы cookie и информацию об аутентификации HTTP. Если вы хотите отправить файл cookie на сервер, с одной стороны, сервер должен согласиться указать поле Access-Control-Allow-Credentials.

Access-Control-Allow-Credentials: true
Скопировать код

С другой стороны, разработчик должен открыть атрибут withCredentials в запросе AJAX.

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
Скопировать код

В противном случае, даже если сервер соглашается отправлять файлы cookie, браузер не отправляет их. Или сервер требует установки файлов cookie, и браузер также не будет их обрабатывать. Однако, если вы опустите параметр withCredentials, некоторые браузеры все равно будут отправлять файлы cookie вместе. В настоящее время вы можете явно закрыть с помощью Credentials.

xhr.withCredentials = false;
Скопировать код

нужно знать,Если вы хотите отправлять файлы cookie, Access-Control-Allow-Origin нельзя установить как звездочку., Необходимо указать четкое доменное имя, которое соответствует запрашиваемой странице. В то же время файлы cookie по-прежнему следуют политике одного и того же происхождения, будут загружены только файлы cookie, установленные с доменным именем сервера, файлы cookie других доменных имен не будут загружены, а файл document.cookie в исходном коде веб-страницы (кросс-источник) не может быть прочитан под доменным именем сервера. Печенье.

Четыре, непростой запрос

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

Непростые запросы — это те, которые предъявляют особые требования к серверу, например, метод запроса — PUT или DELETE, или тип поля Content-Type — application / json.

Для непростых запросов CORS запрос HTTP-запроса будет добавлен перед формальным обменом данными, который называется «предполетным» запросом (предполетным). Сначала браузер спрашивает сервер, находится ли доменное имя текущей веб-страницы в списке разрешений сервера, и какие HTTP-команды и поля заголовка можно использовать. Браузер отправит формальный запрос XMLHttpRequest только в случае положительного ответа, в противном случае он сообщит об ошибке. Ниже приведен сценарий JavaScript для браузера.

var url = 'url';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('Access-Control-Allow-Headers', 'X-PINGOTHER');
xhr.send();
Скопировать код

В приведенном выше коде метод HTTP-запроса — PUT, и отправляется настраиваемая информация заголовка Access-Control-Allow-Headers. Браузер обнаруживает, что это непростой запрос, и автоматически отправляет запрос «предварительной проверки», прося сервер подтвердить, что его можно запросить. Ниже приводится информация HTTP-заголовка этого «предпечатного» запроса.

OPTIONS /cors HTTP/1.1
Origin: xxx
Access-Control-Request-Method: OPTIONS
Access-Control-Request-Headers: X-PINGOTHER
Host: xxx
User-Agent: Mozilla/5.0...
Скопировать код

Метод запроса для запроса «предварительной проверки» — OPTIONS, что означает, что этот запрос предназначен для запроса. В информации заголовка ключевым полем является Origin, который указывает, из какого источника исходит запрос. Помимо поля «Источник», информация заголовка запроса «предпечатная проверка» включает два специальных поля. (1) Access-Control-Request-Method Это поле является обязательным и используется для перечисления, какой протокол HTTP будет использоваться в запросе CORS браузера. Метод, приведенный выше пример — PUT. (2) Access-Control-Request-Headers Это поле представляет собой строку, разделенную запятыми, которая указывает дополнительные заголовки, отправленные запросом CORS браузера. Информационное поле, приведенный выше пример — X-Custom-Header.

4.2 Ответ на предполетный запрос

После того как сервер получил запрос на предварительную проверку, он проверилИсточник, Access-Control-Request-Method и Access-Control-Request-HeadersПосле этого поля подтвердите, что запросы к другим источникам разрешены, и вы можете ответить.

Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:access-control-allow-headers
Access-Control-Allow-Methods:POST,OPTIONS
Access-Control-Allow-Origin:http://newsky.meili-inc.com
Access-Control-Max-Age:1800
Allow:GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Скопировать код

В приведенном выше ответе HTTP ключом является поле Access-Control-Allow-Origin, что означает, что http://newsky.meili-inc.com может запрашивать данные. В этом поле также можно установить звездочку, что означает, что любой запрос на другой источник согласован.

Access-Control-Allow-Origin: *
Скопировать код

Если браузер отклоняет запрос «предпечатной проверки», он вернет обычный HTTP-ответ, но без каких-либо полей заголовка, связанных с CORS. В это время браузер определит, что сервер не согласен с запросом предварительной проверки, поэтому запускается ошибка, которая фиксируется функцией обратного вызова onerror объекта XMLHttpRequest. Консоль распечатает следующее сообщение об ошибке

XMLHttpRequest cannot loadhttp://newsky.meili-inc.com
Origin http://newsky.meili-inc.com is not allowed by Access-Control-Allow-Origin.
Скопировать код

(1) Access-Control-Allow-Methods Это поле является обязательным, и его значение представляет собой разделенную запятыми строку, указывающую все методы междоменного запроса, поддерживаемые сервером. Обратите внимание, что возвращаются все поддерживаемые методы, а не только метод, запрошенный браузером. Это сделано для того, чтобы избежать множественных предварительных запросов. (2) Access-Control-Allow-Headers Если запрос браузера включает поле Access-Control-Request-Headers, тогда Access-Control-Allow -Заголовки обязательны. Это также строка, разделенная запятыми, указывающая все поля заголовка, поддерживаемые сервером, не ограничиваясь полями, запрошенными браузером в «предварительной проверке». (3) Access-Control-Allow-Credentials Это поле имеет то же значение, что и в простом запросе. (4)Access-Control-Max-Age Это поле является необязательным и используется для указания срока действия этого запроса предварительной проверки в секундах. В приведенном выше результате срок действия составляет 1800 с, то есть ответ может быть кэширован на 1800 с. В течение этого периодаНе нужно проводить еще одну предварительную проверку, пожалуйстапопрошайничество.

4.3 Обычные запросы и ответы от браузера

После того, как сервер передал запрос «предварительной проверки», каждый последующий нормальный запрос CORS от браузера будет таким же, как простой запрос, с полем информации заголовка Origin. Ответ сервера также будет иметь поле заголовка Access-Control-Allow-Origin.

Пятый, сравнение с JSONP

CORS имеет ту же цель, что и JSONP, но более мощный, чем JSONP: JSONP поддерживает только запросы GET, CORS поддерживает все типы HTTP-запросов Преимущества JSONP Поддерживает старые браузеры

На чтение 12 мин Просмотров 1.4к. Опубликовано 22.04.2021

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

CORS — это ответ.

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

Сегодня мы подробно рассмотрим CORS и узнаем, как активировать его в различных интерфейсных фреймворках.

Содержание

  1. Что такое CORS?
  2. Как работает CORS?
  3. Типы запросов CORS
  4. Простые просьбы
  5. GET
  6. HEAD
  7. POST
  8. Предварительные запросы
  9. Фоновый процесс предполетного запроса
  10. Приложение Nodejs Express
  11. Flask
  12. Apache
  13. Приложения Spring Boot на Kotlin
  14. Nginx

Что такое CORS?

Совместное использование ресурсов между разными источниками (CORS) — это механизм браузера, который позволяет веб-странице использовать ресурсы и данные с других страниц или доменов.

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

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

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

Что такое происхождение?

Происхождение относится к 3 частям: протоколу, хосту и номеру порта. Протокол относится к протоколу прикладного уровня, часто HTTP. Хост — это основной домен сайта, к которому относятся все страницы, например, Educative.io. Наконец, номер порта — это конечная точка связи для запроса, по умолчанию это порт 80.

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

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

Живой пример CORS

Самый распространенный пример CORS — это реклама на неродных сайтах.

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

Вместо этого вся реклама хранится на серверах рекламной компании. Рекламная компания предоставила доступ для просмотра к YouTube, чтобы веб-страница YouTube могла воспроизводить сохраненное рекламное видео Android.

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

Какие активы может запрашивать CORS?

Сайты используют запросы CORS для загрузки:

  • Запросы на получение или HTTP-запросы, напримерXMLHTTPRequests
  • Веб-шрифты и шрифты TrueType доступны только для межсайтовой загрузки
  • Текстуры Web GL
  • Изображения и видео
  • CSS-формы

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

Нравится статья? Прокрутите вниз, чтобы подписаться на нашу бесплатную новостную рассылку, выходящую два раза в месяц.

Как работает CORS?

CORS добавляет новые заголовки HTTP в список стандартных заголовков. Новые заголовки CORS позволяют локальному серверу хранить список разрешенных источников.

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

Есть много разных типов заголовков ответов, которые обеспечивают разные уровни доступа. Вот еще несколько примеров заголовков CORS HTTP:

  • Access-Control-Allow-Credentials
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Origin

Когда веб-браузер хочет получить доступ к сайту, он отправляет серверу сайта GETзапрос CORS. Если разрешено, GETзапрос позволит браузеру просматривать страницу, но не более того.

Большинство серверов разрешают GETзапросы из любого источника, но блокируют другие типы запросов.

Большинство серверов разрешают GET

Сервер либо отправит обратно значение подстановочного знака *, что означает неограниченный доступ к запрошенным данным, либо сервер проверит список разрешенных источников.

Если источник запроса находится в списке, веб-странице разрешено просматривать веб-страницу, и сервер повторяет имя разрешенного источника.

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

Типы запросов CORS

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

Эти разные запросы существуют, потому что мы можем захотеть предоставить разные уровни доступа в зависимости от источника. Возможно, мы хотели бы, чтобы все GETзапросы были удовлетворены, но только наша партнерская рекламная компания может редактировать активы.

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

Большинство запросов делятся на две основные категории:

  • Простые запросы : эти запросы не запускают предпечатную проверку и используют только заголовки CORS из списка надежных отправителей.
  • Запросы предварительной проверки : эти запросы отправляют сообщение » предварительной проверки «, в котором излагается, что запрашивающая сторона хотела бы сделать перед исходным запросом. Запрошенный сервер просматривает это предварительное сообщение, чтобы убедиться, что запрос безопасен.

Простые просьбы

Простые запросы не требуют предполетной проверки и использовать один из трех способов: GET, POSTи HEAD. Эти запросы поступают до того, как был изобретен CORS, и поэтому им разрешено пропустить предварительную проверку CORS.

GET

GETЗапрос просит, чтобы посмотреть представление общего файла данных с определенным URL. Его также можно использовать для запуска загрузки файлов.

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

GET /index.html

HEAD

HEADЗапрос просмотрит заголовки, которые будут отправляться с GETпросьбой. Он используется для выборки контента, существующего по определенному URL-адресу, без доступа к нему.

Например, вы можете HEADзагрузить URL-адрес, чтобы получить его Content-Lengthзаголовок. Это позволит вам узнать размер загружаемого файла, прежде чем вы согласитесь его загрузить.

HEAD /index.html

POST

POSTЗапрос просит передать данные на запрашиваемый сервер, что может привести к изменению сервера. Если POSTзапрос запускается несколько раз, он может иметь неожиданное поведение.

Примером этого является добавление комментария в ветку форума.

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

POST /test HTTP/1.1
Host: foo.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2

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

Некоторые методы генерируют дополнительный предварительный запрос, который отправляется перед исходным запросом. Запросы предпечатной проверки автоматически генерируются с помощью OPTIONSметода для функций, которые могут влиять на пользовательские данные или вносить серьезные изменения в сервер.

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

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

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

Самый распространенный метод DELETEс предварительным запуском — это удаление выбранного файла или ресурса с сервера.

Фоновый процесс предполетного запроса

Предварительный запрос включает происхождение отправителя запроса и желаемый метод, указанный с помощью Access-Control-Request-Method. Сервер анализирует предполетный запрос, чтобы проверить, есть ли у этого источника доступ для выполнения такого метода.

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

В противном случае исходный запрос игнорируется.

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

Вы можете увидеть дату истечения срока действия разрешения, проверив значение Access-Control-Max-Age.

Браузер запрашивающей стороны может затем кэшировать это предварительное одобрение, пока оно действительно. Вы можете увидеть дату истечения срока действия разрешения, проверив значение Access-Control-Max-Age.

Краткое руководство по внедрению CORS

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

Приложение Nodejs Express

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match the domain you will make the request from
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});
app.get('/', function(req, res, next) {
  // Handle the get for this route
});
app.post('/', function(req, res, next) {
 // Handle the post for this route
});

Flask

Установите пакет:

$ pip install -U flask-cors

Затем добавьте его в свое приложение Flask:

# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)

Apache

Добавьте следующую строку внутри либо , , или разделы вашего сервера конфигурации.

Header set Access-Control-Allow-Origin "*"

Чтобы убедиться, что изменения применяются правильно, запустите, а apachectl -tзатем перезагрузите Apache, используя sudo service apache2 reload.

Приложения Spring Boot на Kotlin

Следующий блок кода Kotlin включает CORS в приложениях Spring Boot.

import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
@Component
class CorsFilter : WebFilter {
    override fun filter(ctx: ServerWebExchange?, chain: WebFilterChain?): Mono<Void> {
        if (ctx != null) {
            ctx.response.headers.add("Access-Control-Allow-Origin", "*")
            ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
            ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
            if (ctx.request.method == HttpMethod.OPTIONS) {
                ctx.response.headers.add("Access-Control-Max-Age", "1728000")
                ctx.response.statusCode = HttpStatus.NO_CONTENT
                return Mono.empty()
            } else {
                ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
                return chain?.filter(ctx) ?: Mono.empty()
            }
        } else {
            return chain?.filter(ctx) ?: Mono.empty()
        }
    }
}

Nginx

Следующий блок кода включает CORS с поддержкой предпечатных запросов.

#
# Wide-open CORS config for nginx
#
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell the client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' ;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
}

#node.js #reactjs #socket.io

Вопрос:

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

Эта ошибка несколько раз появляется в консоли.

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

 cors: {
    origin: "*",
    methods: ["GET", "POST"],
    allowedHeaders: ["my-custom-header"],
    credentials: true
  }
 

Но эта ошибка привела к другой проблеме, и теперь мой токен на предъявителя не проходит проверку подлинности.

введите описание изображения здесь

Я прилагаю код server.js файл ниже. Был бы очень признателен, если бы кто-нибудь помог мне понять ошибку и решения.

 const express = require("express");
const bodyParser = require("body-parser");
const util = require("util");
const request = require("request");
const path = require("path");
const socketIo = require("socket.io");
const http = require("http");

const app = express();
let port = process.env.PORT || 3000;
const post = util.promisify(request.post);
const get = util.promisify(request.get);

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const server = http.createServer(app);
const io = socketIo(server);


const BEARER_TOKEN = process.env.TWITTER_BEARER_TOKEN;

let timeout = 0;

const streamURL = new URL(
  "https://api.twitter.com/2/tweets/search/stream?tweet.fields=context_annotationsamp;expansions=author_id"
);

const rulesURL = new URL(
  "https://api.twitter.com/2/tweets/search/stream/rules"
);

const errorMessage = {
  title: "Please Wait",
  detail: "Waiting for new Tweets to be posted...",
};

const authMessage = {
  title: "Could not authenticate",
  details: [
    `Please make sure your bearer token is correct. 
      If using Glitch, remix this app and add it to the .env file`,
  ],
  type: "https://developer.twitter.com/en/docs/authentication",
};

const sleep = async (delay) => {
  return new Promise((resolve) => setTimeout(() => resolve(true), delay));
};

app.get("/api/rules", async (req, res) => {
  if (!BEARER_TOKEN) {
    res.status(400).send(authMessage);
    return;
  }

  const token = BEARER_TOKEN;
  const requestConfig = {
    url: rulesURL,
    auth: {
      bearer: token,
    },
    json: true,
  };

  try {
    const response = await get(requestConfig);

    if (response.statusCode !== 200) {
      if (response.statusCode === 403) {
        res.status(403).send(response.body);
        return;
      } else {
        throw new Error(response.body.error.message);
      }
    }

    res.send(response);
    return;
  } catch (e) {
    res.send(e);
    return;
  }
});

app.post("/api/rules", async (req, res) => {
  if (!BEARER_TOKEN) {
    res.status(400).send(authMessage);
    return;
  }

  const token = BEARER_TOKEN;
  const requestConfig = {
    url: rulesURL,
    auth: {
      bearer: token,
    },
    json: req.body,
  };

  try {
    const response = await post(requestConfig);

    if (response.statusCode === 200 || response.statusCode === 201) {
      res.send(response);
      return;
    } else {
      throw new Error(response);
    }
  } catch (e) {
    res.send(e);
    return;
  }
});

const streamTweets = (socket, token) => {
  let stream;

  const config = {
    url: streamURL,
    auth: {
      bearer: token,
    },
    timeout: 31000,
  };

  try {
    const stream = request.get(config);

    stream
      .on("data", (data) => {
        try {
          const json = JSON.parse(data);
          if (json.connection_issue) {
            socket.emit("error", json);
            reconnect(stream, socket, token);
          } else {
            if (json.data) {
              socket.emit("tweet", json);
            } else {
              socket.emit("authError", json);
            }
          }
        } catch (e) {
          socket.emit("heartbeat");
        }
      })
      .on("error", (error) => {
        // Connection timed out
        socket.emit("error", errorMessage);
        reconnect(stream, socket, token);
      });
  } catch (e) {
    socket.emit("authError", authMessage);
  }
};

const reconnect = async (stream, socket, token) => {
  timeout  ;
  stream.abort();
  await sleep(2 ** timeout * 1000);
  streamTweets(socket, token);
};

io.on("connection", async (socket) => {
  try {
    const token = BEARER_TOKEN;
    io.emit("connect", "Client connected");
    const stream = streamTweets(io, token);
  } catch (e) {
    io.emit("authError", authMessage);
  }
});

console.log("NODE_ENV is", process.env.NODE_ENV);

if (process.env.NODE_ENV === "production") {
  app.use(express.static(path.join(__dirname, "../build")));
  app.get("*", (request, res) => {
    res.sendFile(path.join(__dirname, "../build", "index.html"));
  });
} else {
  port = 3001;
}

server.listen(port, () => console.log(`Listening on port ${port}`));
 

Комментарии:

1. Вы не можете использовать подстановочные знаки происхождения и учетные данные одновременно. Если вы хотите использовать учетные данные, укажите домены явно.

Ответ №1:

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

 cors: {
    origin: "*", // <-- set origin explicitly.
    methods: ["GET", "POST"],
    allowedHeaders: ["my-custom-header"],
    credentials: true
}
 

Errors: CORSDidNotSucceed

Причина:Запрос CORS не увенчался успехом

Reason

Reason: CORS request did not succeed

Что пошло не так?

HTTP запрос , который использует CO не удался , так как соединение HTTP потерпело неудачу в любой сети или на уровне протокола. Ошибка не связана напрямую с CORS, но является какой-то фундаментальной сетевой ошибкой.

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

Другие возможные причины включают:

  • Попытка получить доступ к https — ресурсу с недопустимым сертификатом вызовет эту ошибку.
  • Попытка получить доступ к http — ресурсу со страницы с источником https также вызовет эту ошибку.
  • Начиная с Firefox 68, https — страницам не разрешен доступ к http://localhost , хотя это может быть изменено ошибкой 1488740 .
  • Сервер не ответил на фактический запрос (даже если он ответил на запрос предварительной проверки ). Одним из сценариев может быть разработка службы HTTP, которая запаниковала, не вернув никаких данных.

See also

  • CORS errors
  • Glossary: CORS
  • CORS introduction


HTTP

  • Errors

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

  • Errors: CORSAllowOriginNotMatchingOrigin

    Происхождение,делающее запрос,не соответствует разрешенному заголовком Access-Control-Allow-Origin.

  • Errors: CORSDisabled

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

  • Errors: CORSExternalRedirectNotAllowed

    На CORS-запрос сервер ответил HTTP-перенаправлением URL-адреса другого происхождения,что недопустимо при запросах.

Понравилась статья? Поделить с друзьями:
  • Ошибка соединения 0x2202 kyocera при сканировании на почту
  • Ошибка совместного доступа к хранилищу конфигурации 1с
  • Ошибка снижение мощности на volvo fh 12
  • Ошибка соединения 0x2201
  • Ошибка создателя книга