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

Cea*_*sta 12 google-chrome google-chrome-extension cors cross-origin-read-blocking

我正在开发一个Chrome扩展程序,该扩展程序可以将某些网站的请求发送到我控制的API。直到Chrome 73,该扩展程序才能正常工作。升级到Chrome 73之后,我开始出现以下错误:

跨域读取阻止(CORB)使用MIME类型application / json 阻止了跨源响应 http:// localhost:3000 / api / users / 1

根据Chrome关于CORB的文档,如果满足以下所有条件,则CORB将阻止请求的响应:

  1. 该资源是“数据资源”。具体来说,内容类型为HTML,XML,JSON

  2. 服务器以X-Content-Type-Options: nosniff标头作为响应,或者如果省略标头,则Chrome通过检查文件来检测内容类型是HTML,XML还是JSON之一

  3. CORS不允许显式访问资源

另外,根据“从幽灵和熔化的教训”(谷歌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
Run Code Online (Sandbox Code Playgroud)

其次,我fetch()将扩展名的调用更新为如下形式:

fetch(url, { credentials: 'include', mode: 'cors' })
Run Code Online (Sandbox Code Playgroud)

但是,这些更改不起作用。我该如何更改以使我的请求不会被CORB阻止?

Cea*_*sta 14

根据“ Chrome扩展程序内容脚本中对跨域请求的更改”中的示例,我用具有类似API fetch的新方法替换了所有的调用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;
});
Run Code Online (Sandbox Code Playgroud)

这是我能够对应用程序进行修复的最小更改集。(请注意,扩展程序和后台页面只能在它们之间传递JSON可序列化的对象,因此我们不能简单地将Fetch API Response对象从后台页面传递给扩展程序。)

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

  • 我不确定使用 `response.text()`、`new Blob([response.body])` 是否是重建 Response 对象的最可靠方法,但它适用于我只处理的应用程序JSON 响应。我也不确定如何传递标题。 (3认同)
  • 我们如何对第三方组件进行自己的API调用:// (2认同)

小智 5

请参阅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 => ...)
Run Code Online (Sandbox Code Playgroud)

新的内容脚本,要求其后台页面获取数据:

chrome.runtime.sendMessage(
    {contentScriptQuery: "queryPrice", itemId: 12345},
    price => ...);
Run Code Online (Sandbox Code Playgroud)

新的扩展后台页面,从已知 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.
    }
  });
Run Code Online (Sandbox Code Playgroud)

允许manifest.json 中的 URL (更多信息):

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