I am developing a Chrome extension which makes requests from certain websites to an API I control. Until Chrome 73, the extension worked correctly. After upgrading to Chrome 73, I started getting the following error:
Cross-Origin Read Blocking (CORB) blocked cross origin response http://localhost:3000/api/users/1 with MIME type application/json
According to Chrome's documentation on CORB, CORB will block the response of a request if all of the following are true:
The resource is a "data resource". Specifically, the content type is HTML, XML, JSON
The server responds with an X-Content-Type-Options: nosniff
header, or if this header is omitted, Chrome detects the content type is one of HTML, XML, or JSON from inspecting the file
CORS does not explicitly allow access to the resource
Also, according to "Lessons from Spectre and Meltdown" (Google I/O 2018), it seems like it may be important to add mode: cors
to fetch
invocations, i.e., fetch(url, { mode: 'cors' })
.
To try to fix this, I made the following changes:
First, I added the following headers to all responses from my 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
Second, I updated my fetch()
invocation on the extension to look like this:
fetch(url, { credentials: 'include', mode: 'cors' })
However, these changes didn't work. What can I change to make my request not be blocked by CORB?
Cross-Origin Read Blocking (CORB) is an algorithm that can identify and block dubious cross-origin resource loads in web browsers before they reach the web page. CORB reduces the risk of leaking sensitive data by keeping it further from cross-origin web pages.
In rare cases, the CORB warning message may indicate a problem on a website, which may disrupt its behavior when certain responses are blocked. For example, a response served with a "X-Content-Type-Options: nosniff" response header and an incorrect "Content-Type" response header may be blocked.
Cross-Origin Read Blocking, or CORB, is a new security feature that prevents the contents of balance. json from ever entering the memory of the renderer process memory based on its MIME type.
Based on the examples in "Changes to Cross-Origin Requests in Chrome Extension Content Scripts", I replaced all invocations of fetch
with a new method fetchResource
, that has a similar API, but delegates the fetch
call to the background page:
// 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; });
This is the smallest set of changes I was able to make to my app that fixes the issue. (Note, extensions and background pages can only pass JSON-serializable objects between them, so we cannot simply pass the Fetch API Response object from the background page to the extension.)
Background pages are not affected by CORS or CORB, so the browser no longer blocks the responses from the API.
See https://www.chromium.org/Home/chromium-security/extension-content-script-fetches
To improve security, cross-origin fetches from content scripts are disallowed in Chrome Extensions since Chrome 85. Such requests can be made from extension background script instead, and relayed to content scripts when needed.
You can do that to avoid Cross-Origin.
Old content script, making a cross-origin fetch:
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 => ...)
New content script, asking its background page to fetch the data instead:
chrome.runtime.sendMessage( {contentScriptQuery: "queryPrice", itemId: 12345}, price => ...);
New extension background page, fetching from a known URL and relaying data:
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. } });
Allow the URL in manifest.json (more info):
"permissions": ["https://another-site.com/"]
"host_permissions": ["https://another-site.com/"]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With