Как запретить CORB блокировать запросы к ресурсам данных, которые отвечают заголовками CORS?

Я разрабатываю расширение Chrome, которое отправляет запросы с определенных веб-сайтов на контролируемый мной API. До Chrome 73 расширение работало корректно. После обновления до Chrome 73 я начал получать следующую ошибку:

Блокировка чтения из разных источников (CORB) заблокировала ответ из разных источников http://localhost:3000/api/users/1 с типом MIME application / json

Согласно документации Chrome по CORB, CORB блокирует ответ запрос, если все следующие верны:

  1. Ресурс - это «ресурс данных». В частности, тип контента - HTML, XML, JSON.

  2. Сервер отвечает заголовком X-Content-Type-Options: nosniff, или, если этот заголовок опущен, Chrome определяет тип содержимого - HTML, XML или JSON, проверяя файл.

  3. CORS явно не разрешает доступ к ресурсу

Кроме того, согласно «Уроки от Spectre и Meltdown» (Google I / O 2018), кажется, что может быть важно добавить mode: cors к fetch вызовам, т. е. fetch(url, { mode: 'cors' }).

Чтобы попытаться исправить это, я внес следующие изменения:

Во-первых, я добавил следующие заголовки ко всем ответам моего API:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Origin: https://www.example.com

Во-вторых, я обновил вызов fetch() в расширении, чтобы он выглядел так:

fetch(url, { credentials: 'include', mode: 'cors' })

Однако эти изменения не сработали. Что я могу изменить, чтобы мой запрос не блокировался CORB?


person Ceasar Bautista    schedule 18.03.2019    source источник
comment
См. Решения в статье Google для конкретных расширений, которая отличается.   -  person wOxxOm    schedule 18.03.2019
comment
Отлично спасибо. Можете ли вы сделать свой комментарий ответом, чтобы я мог его принять?   -  person Ceasar Bautista    schedule 18.03.2019
comment
Я думаю, что будет лучше, если вы опубликуете ответ - возможно, с некоторыми дополнительными деталями, которые вы сочтете актуальными, - поскольку вы знаете об этом больше. Я знал только о статье, а не о деталях.   -  person wOxxOm    schedule 18.03.2019
comment
См. Также stackoverflow.com/questions / 55153960 / и stackoverflow.com/questions/55153888/   -  person sideshowbarker    schedule 18.03.2019
comment
Хотя для решения проблемы достаточно и уместно использовать фоновую страницу, я все еще не понимаю, почему Chrome заблокировал мои запросы от расширения. В статьях об изменениях запросов между источниками в сценариях содержимого расширений Chrome написано: «Чтобы смягчить эти проблемы, будущие версии Chrome будут ограничивать сценарии содержимого теми же запросами, которые может выполнять сама страница. Это наводит на мысль, что запросы на перекрестное происхождение все еще возможны из расширения, но они должны следовать CORS. Разве мой запрос не должен был быть успешным, так как я добавил заголовки CORS к своим ответам?   -  person Ceasar Bautista    schedule 18.03.2019
comment
Мне тоже был бы интересен ответ на этот вопрос. Chrome 73 с включенной NetworkService, по-видимому, просто не выполняет предварительные запросы CORS для запросов xhr, сделанных из сценария содержимого, даже если запрос требует CORS и инициирует предварительный запрос, если он сделан со страницы хоста. Неужели это ошибка Хрома? Согласно документации, их намерение состоит в том, чтобы сделать сценарии содержимого подчиненными тем же правилам запросов, что и страница, на которой они работают. Если запросы x-origin, сделанные со страницы, запускают предварительную проверку, а запросы из сценария содержимого - нет, похоже, что это нарушает это намерение.   -  person Simon Woolf    schedule 19.03.2019
comment
Не только вы читаете chromium.org/Home/ chromium-security / (несколько раз) и не понимали, что они пытались сказать. В чем разница между изменениями в Chrome 73 и Chrome 85? Chrome 73: Chrome убрал возможность делать запросы из разных источников в сценариях контента. Chrome 85: в Chrome удалена возможность обхода CORS в запросах между источниками из скриптов контента. Разве эти две фразы не означают одно и то же?   -  person traxium    schedule 01.10.2020


Ответы (3)


На основе примеров, приведенных в разделе "Изменения запросов между источниками в расширении Chrome Скрипты содержимого ", я заменил все вызовы fetch новым методом fetchResource, который имеет аналогичный API, но делегирует вызов fetch фоновой странице:

// contentScript.js
function fetchResource(input, init) {
  return new Promise((resolve, reject) => {
    chrome.runtime.sendMessage({input, init}, messageResponse => {
      const [response, error] = messageResponse;
      if (response === null) {
        reject(error);
      } else {
        // Use undefined on a 204 - No Content
        const body = response.body ? new Blob([response.body]) : undefined;
        resolve(new Response(body, {
          status: response.status,
          statusText: response.statusText,
        }));
      }
    });
  });
}

// background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  fetch(request.input, request.init).then(function(response) {
    return response.text().then(function(text) {
      sendResponse([{
        body: text,
        status: response.status,
        statusText: response.statusText,
      }, null]);
    });
  }, function(error) {
    sendResponse([null, error]);
  });
  return true;
});

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

CORS или CORB не влияют на фоновые страницы, поэтому браузер больше не блокирует ответы от API.

person Ceasar Bautista    schedule 18.03.2019
comment
Я не уверен, что использование response.text(), new Blob([response.body]) - самый верный способ перестроить объект Response, но он работает в моем приложении, где я имею дело только с ответами JSON. Я также не уверен, как передавать заголовки. - person Ceasar Bautista; 18.03.2019
comment
Что мы можем сделать со сторонними компонентами, которые делают свои собственные вызовы API: / - person Roman Scher; 22.03.2019
comment
Хороший, копируемый и вставляемый код. Кстати, вы должны принять это как ответ. - person Michael Yaworski; 31.05.2019
comment
Не знаю почему, но с Chrome 81 это, похоже, не так. Кажется, что фоновым скриптам запрещено делать fetch вызовы CORS. - person avalanche1; 22.04.2020
comment
С этим изменением я получаю следующую ошибку: Unchecked runtime.lastError: порт сообщения закрыт до получения ответа. Есть мысли, как это исправить? - person Persistent Plants; 02.09.2020

См. https://www.chromium.org/Home/chromium-security/extension-content-script-fetches

Для повышения безопасности выборка из разных источников из сценариев содержимого запрещена в расширениях Chrome, начиная с Chrome 85. Такие запросы могут выполняться из фонового сценария расширения и при необходимости ретранслироваться в сценарии содержимого.

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

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

var itemId = 12345;
var url = "https://another-site.com/price-query?itemId=" +
         encodeURIComponent(request.itemId);
fetch(url)
  .then(response => response.text())
  .then(text => parsePrice(text))
  .then(price => ...)
  .catch(error => ...)

Сценарий нового содержания, запрашивающий данные для фоновой страницы:

chrome.runtime.sendMessage(
    {contentScriptQuery: "queryPrice", itemId: 12345},
    price => ...);

Новая фоновая страница расширения, загрузка по известному URL и передача данных:

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.contentScriptQuery == "queryPrice") {
      var url = "https://another-site.com/price-query?itemId=" +
              encodeURIComponent(request.itemId);
      fetch(url)
          .then(response => response.text())
          .then(text => parsePrice(text))
          .then(price => sendResponse(price))
          .catch(error => ...)
      return true;  // Will respond asynchronously.
    }
  });

Разрешите URL-адрес в manifest.json (дополнительная информация):

  • ManifestV2 (классический): "permissions": ["https://another-site.com/"]
  • ManifestV3 (предстоящий ): "host_permissions": ["https://another-site.com/"]
person 李方郑    schedule 22.03.2019

Временное решение: отключите CORB с помощью браузера команд запуска --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating

Пример команды запуска в Linux.

Для Chrome:

chrome %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating

Для Chromium:

chromium-browser %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating

Аналогичный вопрос. < / а>

Источник.

person Epexa    schedule 25.03.2019