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?
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):
ManifestV2 (classic): "permissions": ["https://another-site.com/"]
ManifestV3 (upcoming): "host_permissions": ["https://another-site.com/"]
Temporary solution: disable CORB with run command browser
--disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Example run command on Linux.
For Chrome:
chrome %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
For Chromium:
chromium-browser %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Similar question.
Source.
Related
Problem
I use axios (that is not important, any alternative is ok) to make my HTTP calls to an external API.
When I do these HTTP calls via Postman to the API directly, I get this header at the API call response:
{ ..., Content-Type: application/json; charset=windows-1253, ... }
I do these HTTP calls via my NodeJS app like this:
router.get('/data',
async (req, res) => {
try {
let customers = await axios
.post(URL, {
...
})
res.send(customers.data)
} catch (error) {
res.send({ error: error })
}
}
)
and when I call it from Postman I get this header at the API call response:
{ ..., Content-Type: application/json; charset=utf-8, ... }
Pay attention that in the first case, the charset is (correctly) windows-1253, wherein the second case it is utf-8
Question
How can I set the charset to the response to be windows-1253 OR is there any way to decode utf-8 into windows-1253 in nodejs?
I found out about iconv, but as stated in this repository I have to install external tools to use it which is not my favorite think to do, so I would appreciate any alternatives.
The solution was to decode into ISO-8859-7 like this:
let customers = await axios.request({
method: 'POST',
url: url,
data: {
...
},
responseType: 'arraybuffer',
reponseEncoding: 'binary'
});
const decoder = new TextDecoder('ISO-8859-7');
let decodedCustomers = decoder.decode(customers.data)
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?
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):
ManifestV2 (classic): "permissions": ["https://another-site.com/"]
ManifestV3 (upcoming): "host_permissions": ["https://another-site.com/"]
Temporary solution: disable CORB with run command browser
--disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Example run command on Linux.
For Chrome:
chrome %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
For Chromium:
chromium-browser %U --disable-features=CrossSiteDocumentBlockingAlways,CrossSiteDocumentBlockingIfIsolating
Similar question.
Source.
Please read, this is different!
I've used Firebase Functions previously and solved this issue by adding this code:
const cors = require('cors')({ origin: true });
return cors(req, res, () => {
let format = req.query.format;
if (!format) {
format = req.body.format;
}
const formattedDate = moment().format(format);
console.log('Sending Formatted date:', formattedDate);
res.status(200).send(formattedDate);
});
But now I'm working on a new project, and I'm getting this error no matter what I try to do.
I have read and tried the solutions in over 20 other questions here on stackoverflow and around the internet, and none of them work now.
So I went to firebase's GitHub, downloaded the date example (has the recommended cors fix implemented) and deployed it.
And I still get the same error!
Access to fetch at 'https://us-central1-generation-y-members.cloudfunctions.net/date' from origin
'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The function can be called from postman and does work. https://i.imgur.com/YTi1PpQ.png
I've upgraded my project to the blaze plan (didn't help).
I've tried changing to origin: "http://localhost:3000" instead of origin: true, didn't help at all.
I've tried uploading my react app to a server and calling from there, same result (http, not https) - even when setting origin: "http://my-site.com"
Any pointers would be highly appreciated.
The issue is that the query being made from the localhost is missing the CORS headers.
On your client side application you need to add the following headers to be able to perform the CORS calls.
'Access-Control-Allow-Origin', '*'
'Access-Control-Allow-Headers', 'Content-Type'
If you are using jav ascript on the client side application this can be done with the following code, according to the libraries you are using.
xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
xhr.setRequestHeader('Access-Control-Allow-Headers', 'Content-Type');
I found the solution so I'll post it here for anyone that has the same error and doesn't know why it's happening:
This is the code I'm using now, the issue was not using JSON.stringify() when setting the body for the request.
let body: any = {};
body.name = currentUser.name;
body.email = currentUser.email;
body.password = generatedPassword;
body.message = '';
body.number = randomNumber;
const requestOptions: any = {
method: 'POST',
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body), // ISSUE WAS HERE! I HAD 'body: body'
};
try {
let response = await (await fetch('https://us-central1-generation-y-members.cloudfunctions.net/register', requestOptions)).text();
// let response = await (await fetch('http://localhost:5001/generation-y-members/us-central1/register', requestOptions)).text();
console.log({ response: response });
sentEmails.push(currentUser.email);
} catch (e) {
console.log({ error: e });
}
Good luck!
I try to fetch data from my background script like recommended here but it always got blocked. did i miss something?
Background.js:
chrome.storage.sync.get(['sigurl'], function(result) {
console.log(result.sigurl);
fetch(result.sigurl)
.then((response) => {
return response.text();
})
.then((html) => {
console.log(html);
chrome.storage.sync.set({'sig': html}, function() {});
})
.catch(function(err) {
console.log('Failed to fetch page: ', err);
});
});
Console:
Access to fetch at 'http://example.com/test.html' from origin 'chrome-extension://pbflkjmmkpgddamdcihlbggdccjmjmbk' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
use no-cors mode didn’t work either.
The solution for my problem is adding
"permissions": [
"https://*/"
]
to my manifest.json
Thank you to wOxxOm for pointing me in the right direction.
I face this problem:
XMLHttpRequest cannot load
http://localhost:8000/scripts/advaced_donwload/advancedUpload/vueupload/store.php.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access.
This is my code:
storeMeta(file) {
var fileObject = this.generateFileObject(file)
return new Promise((resolve, reject) => {
this.$http.post('http://localhost:8888/vueupload/store.php', {
name: file.name
}).then((response) => {
fileObject.id = response.body.data.id
resolve(fileObject)
}, () => {
reject(fileObject)
})
})
}
your php server must allow http://localhost:8080 to POST resources. this is done in the server configuration.
You can either completely disable the CORS on the server, enabling all sources to communicate with your server.
Or you can add this header for the server:
Access-Control-Allow-Origin: http://localhost:8080
Or just allow everything
Access-Control-Allow-Origin: *
To do it in on a PHP server, it might be something simple like this:
<?php
header("Access-Control-Allow-Origin: *");
if you are using a framework such as laravel or such, check their documentation in the CORS sections