如何阻止 CORB 阻止对响应 CORS headers 的数据资源的请求?

How to stop CORB from blocking requests to data resources that respond with CORS headers?

我正在开发一个 Chrome 扩展程序,它从某些网站向我控制的 API 发出请求。在 Chrome 73 之前,扩展正常工作。升级到 Chrome 73 后,我开始收到以下错误:

Cross-Origin Read Blocking (CORB) blocked cross origin response http://localhost:3000/api/users/1 with MIME type application/json

根据 Chrome's documentation on CORB,如果满足以下所有条件,CORB 将阻止请求的响应:

  1. 资源是 "data resource"。具体内容类型为HTML、XML、JSON

  2. 服务器以 [​​=13=] header 响应,或者如果此 header 被省略,Chrome 检测到内容类型是 HTML、XML 或 JSON 来自检查文件

  3. CORS 未明确允许访问资源

此外,根据 "Lessons from Spectre and Meltdown" (Google I/O 2018),将 mode: cors 添加到 fetch 调用似乎很重要,即 fetch(url, { mode: 'cors' })

为了解决这个问题,我做了以下更改:

首先,我在 API 的所有回复中添加了以下 header:

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 阻止?

基于 "Changes to Cross-Origin Requests in Chrome Extension Content Scripts" 中的示例,我将 fetch 的所有调用替换为具有类似 API 的新方法 fetchResource,但委托 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-serializable 对象,所以我们不能简单地将后台页面的 Fetch API Response 对象传递给扩展。)

后台页面不受 CORS 或 CORB 的影响,因此浏览器不再阻止来自 API 的响应。

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

为了提高安全性,自 Chrome 85 以来,Chrome 扩展中不允许从内容脚本进行跨源提取。此类请求可以从扩展后台脚本发出,并在以下情况下中继到内容脚本需要。

您可以这样做以避免跨源。

旧内容脚本,进行跨域获取:

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.
    }
  });

允许 manifest.json 中的 URL (more info):

  • ManifestV2(经典):"permissions": ["https://another-site.com/"]
  • ManifestV3 (upcoming): "host_permissions": ["https://another-site.com/"]

临时解决方案:使用 运行 命令浏览器禁用 CORB --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating

Linux 上的示例 运行 命令。

对于Chrome:

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

对于 Chromium:

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

Similar question.

Source