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

Google chrome 如何阻止CORB阻止对响应CORS头的数据资源的请求?,google-chrome,google-chrome-extension,cors,cross-origin-read-blocking,Google Chrome,Google Chrome Extension,Cors,Cross Origin Read Blocking,我正在开发一个Chrome扩展,它可以从某些网站向我控制的API发出请求。在Chrome 73之前,分机工作正常。升级到Chrome 73后,我开始出现以下错误: 跨源读取阻塞(CORB)阻塞的跨源响应 使用MIME类型的application/json 根据,如果满足以下所有条件,CORB将阻止请求响应: 该资源是“数据资源”。具体来说,内容类型是HTML、XML、JSON 服务器以X-Content-Type-Options:nosniff头响应,或者如果省略了此头,Chrome会通过检查文

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

跨源读取阻塞(CORB)阻塞的跨源响应 使用MIME类型的application/json

根据,如果满足以下所有条件,CORB将阻止请求响应:

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

  • 服务器以
    X-Content-Type-Options:nosniff
    头响应,或者如果省略了此头,Chrome会通过检查文件检测到内容类型是HTML、XML或JSON之一

  • CORS不明确允许访问资源

  • 此外,根据,似乎将
    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阻止?

    根据中的示例,我用一个新方法
    fetchResource
    替换了
    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的响应。

    请参阅

    为了提高安全性,自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 => ...);
    
    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.
        }
      });
    
    新内容脚本,要求其后台页面取回数据:

    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 => ...);
    
    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获取并转发数据:

    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 => ...);
    
    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():

    • ManifestV2(经典):
      “权限”:[“https://another-site.com/“]
    • ManifestV3():
      “主机权限”:[“https://another-site.com/“]

    临时解决方案:使用运行命令浏览器禁用CORB
    --禁用功能=交叉站点文档阻止始终,交叉站点文档阻止隔离

    Linux上的run命令示例

    对于铬:

    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 => ...);
    
    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.
        }
      });
    
    chrome%U--禁用功能=交叉站点文档阻止始终,交叉站点文档阻止隔离

    对于铬:

    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 => ...);
    
    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.
        }
      });
    
    chromium浏览器%U--禁用功能=交叉站点文档阻止始终,交叉站点文档阻止隔离


    在谷歌的一篇针对扩展的文章中可以看到解决方案。我认为如果你能给出一个答案——也许有一些你认为相关的额外细节——可能会更好,因为你知道的更多。我只知道这篇文章,不知道它的细节。另请参阅,尽管使用背景页面足以解决问题,但我仍然不明白为什么Chrome阻止了我的扩展请求。“Chrome扩展内容脚本中跨源请求的更改”文章写道,“为了缓解这些担忧,Chrome的未来版本将限制内容脚本与页面本身可以执行的获取相同。”这向我表明,跨源请求仍然可以从扩展中进行,但它们必须遵循CORS。既然我在回复中添加了CORS标题,那么我的请求就不应该成功了吗?我也对这个问题的答案感兴趣。启用NetworkService的Chrome 73似乎只是没有对从内容脚本发出的xhr请求发出CORS飞行前请求,即使该请求需要CORS,并且如果从主机页发出,将触发飞行前请求。这可能是一个Chrome bug?根据文档,他们的意图是使内容脚本“遵守与他们运行的页面相同的请求规则”。如果从页面发出的x-origin请求触发了预飞行,但从内容脚本发出的请求没有触发预飞行,这似乎破坏了该意图。我不确定使用
    response.text()
    new Blob([response.body])
    是否是重建响应对象最可靠的方法,但它在我的应用程序中工作,我只处理JSON响应。我也不知道如何传递头。但是,对于进行自己API调用的第三方组件,我们可以做些什么:/Nice,copy和pastable代码。你应该接受这个答案,顺便说一句,不知道为什么,但因为Chrome81,情况似乎不是这样。后台脚本似乎被禁止进行
    fetch
    调用。通过此更改,我收到以下错误:Unchecked runtime.lastError:消息端口在收到响应之前关闭。有没有想过如何解决这个问题?