提问人:Ceasar 提问时间:3/18/2019 最后编辑:wOxxOmCeasar 更新时间:3/21/2023 访问量:75522
如何在内容脚本中发出跨域请求(尽管 CORS 标头正确,但当前仍被 CORB 阻止)?
How to make a cross-origin request in a content script (currently blocked by CORB despite the correct CORS headers)?
问:
我正在开发一个 Chrome 扩展程序,该扩展程序从某些网站向我控制的 API 发出请求。在 Chrome 73 之前,该扩展程序可以正常工作。升级到 Chrome 73 后,我开始出现以下错误:
跨域读取阻止 (CORB) 阻止了 MIME 类型为 application/json 的跨域响应 http://localhost:3000/api/users/1
根据 Chrome 在 CORB 上的文档,如果满足以下所有条件,CORB 将阻止请求的响应:
资源是“数据资源”。具体来说,内容类型为 HTML、XML、JSON
服务器会以标头进行响应,如果省略此标头,Chrome 会通过检查文件检测到内容类型是 HTML、XML 或 JSON 之一
X-Content-Type-Options: nosniff
CORS 未显式允许访问资源
此外,根据“Lessons from Spectre and 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 阻止?
答:
根据“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;
});
这是我能够对我的应用程序进行修复该问题的最小更改集。(请注意,扩展和后台页面只能在它们之间传递 JSON 可序列化对象,因此我们不能简单地将 Fetch API 响应对象从后台页面传递到扩展。
后台页面不受 CORS 或 CORB 的影响,因此浏览器不再阻止来自 API 的响应。
警告!必须在 manifest.json 中添加 URL:
- 对于 ManifestV3
"host_permissions": ["*://*.example.com/"]
- 对于 ManifestV2 中
"permissions": ["*://*.example.com/"]
评论
response.text()
new Blob([response.body])
fetch
查看 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.
}
});
允许 URL in manifest.json (更多信息):
- ManifestV2(经典):
"permissions": ["https://another-site.com/"]
- ManifestV3(即将推出):
"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
来源。
评论