INFURA / infura

Official Public Repository for INFURA
https://infura.io
381 stars 62 forks source link

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include' #188

Open olegabr opened 5 years ago

olegabr commented 5 years ago

Error in Chrome console:

Access to XMLHttpRequest at 'https://ropsten.infura.io/v3/my-id-here' from origin 'https://olegabr:4443' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Chrome request headers:

General

Request URL: https://ropsten.infura.io/v3/my-id-here
Request Method: OPTIONS
Status Code: 200 
Remote Address: 52.3.78.131:443
Referrer Policy: no-referrer-when-downgrade

Response Headers

access-control-allow-headers: Content-Type
access-control-allow-methods: POST
access-control-allow-origin: *
content-length: 0
date: Fri, 02 Aug 2019 17:52:21 GMT
status: 200
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers

Request Headers

Provisional headers are shown
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Origin: https://olegabr:4443
Referer: https://olegabr:4443/checkout/order-received/36602/?key=wc_order_fQFGD8kzXJKbB
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/75.0.3770.90 Chrome/75.0.3770.90 Safari/537.36
egalano commented 5 years ago

Thanks for reporting @olegabr . We're looking into this

egalano commented 5 years ago

Is this site built using web3js? If so, what version are you using.

olegabr commented 5 years ago

Yes, it uses web3js. Good old 0.20.7 is used.

olegabr commented 5 years ago

still the same problem

egalano commented 5 years ago

@olegabr it seems like the withCredentials attribute is being set on your request when it shouldn’t be. Can you confirm in your code?

olegabr commented 5 years ago

withCredentials no such attribute in my code. Anyway, I've switched to the new stable 1.2 web3.js version and problem disappears. Thank you.

olegabr commented 4 years ago

https://elevato.uk/payments/zamowienie/order-received/506/?key=wc_order_YZH3EpPwfdmKd

it is enough to call this code in a console:

epg.web3.eth.gasPrice

to have this error:

inpage.js:formatted:9351 Access to XMLHttpRequest at 'https://mainnet.infura.io/v3/03a06ac61e0740ed8934ca4bc13c8cf1' from origin 'https://elevato.uk' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
o.send @ inpage.js:formatted:9351
a.send @ inpage.js:formatted:10477
(anonymous) @ inpage.js:formatted:10427
(anonymous) @ VM1730:1
inpage.js:formatted:9351 POST https://mainnet.infura.io/v3/03a06ac61e0740ed8934ca4bc13c8cf1 net::ERR_FAILED
o.send @ inpage.js:formatted:9351
a.send @ inpage.js:formatted:10477
(anonymous) @ inpage.js:formatted:10427
(anonymous) @ VM1730:1
inpage.js:formatted:9353 Uncaught Error: CONNECTION ERROR: Couldn't connect to node https://mainnet.infura.io/v3/03a06ac61e0740ed8934ca4bc13c8cf1.
    at Object.InvalidConnection (inpage.js:formatted:8718)
    at o.send (inpage.js:formatted:9353)
    at a.send (inpage.js:formatted:10477)
    at w.gasPrice (inpage.js:formatted:10427)
    at <anonymous>:1:14

The current active plugin version uses web3.js 0.20.6: https://wordpress.org/plugins/ether-and-erc20-tokens-woocommerce-payment-gateway/ But according the call stack the version from MetaMask is actually used, and it looks like it is 0.20.6 also.

You asked about the withCredentials. I've seen it is set in the web3.js code.

Problem persists regardless if I'm logged into my MetaMask account or not.

ryanschneider commented 4 years ago

Thanks for the report @olegabr, we have a fix to return a non-wildcard CORS header in testing now. However, since there's a small chance that some customer is expecting * we don't want to release this fix going into the weekend, so will plan on releasing it early next week.

olegabr commented 4 years ago

@ryanschneider, thank you for the release plans! Will wait it impatiently :-)

olegabr commented 4 years ago

Look at this also please: https://github.com/trustwallet/trust-web3-provider/issues/76#issuecomment-536158648

ryanschneider commented 4 years ago

@olegabr the fix for this issue has been deployed, let me know if you are still seeing issues.

Thanks, Ryan

ryanschneider commented 4 years ago

Also sorry didn't mean to close the issue before it was deployed, I referenced it in my PR for the backend code change and forgot that if you say "fixes #foo" then the issue is closed when the PR is merged. I'll close this after I get confirmation from you that the issue is resolved.

olegabr commented 4 years ago

Yes, issue still persists:

Screenshot from 2019-09-30 23-52-54

To reproduce, just open the https://elevato.uk/payments/zamowienie/order-received/506/?key=wc_order_YZH3EpPwfdmKd page and type

epg.web3.eth.gasPrice

in a browser console.

ryanschneider commented 4 years ago

Hmm, ok, that's actually a different issue, now that the Access-Control-Allow-Origin header is for your site, it's the fact that the fetch request is being sent withCredentials that's causing the issue, I think this is a bug in web3.js.

I don't want to include return the -Allow-Credentials header unless strictly necessary, because we do not want to encourage Infura users to include their secret for auth from a client-side dapp, so we might need to get creative in how we work around this issue.

Also which browser are you seeing the issue in? I'm using Safari 13.1 and am not getting any issues on your site when running epg.web3.eth.gasPrice in the console:

image

image

Edit: you originally mentioned Chrome, so I'm guessing the issue above was in Chrome as well, I'll see if I can reproduce.

ryanschneider commented 4 years ago

Confirmed I see the same issue in Chrome (Version 77.0.3865.90).

What's interesting is that if I copy the request from Chrome as a CURL, and run it I don't even see any CORS headers on the request:

$ curl 'https://mainnet.infura.io/v3/03a06ac61e0740ed8934ca4bc13c8cf1' -H 'Sec-Fetch-Mode: cors' -H 'Referer: https://elevato.uk/payments/zamowienie/order-received/506/?key=wc_order_YZH3EpPwfdmKd' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' -H 'Content-Type: application/json' --data-binary '{"jsonrpc":"2.0","id":6,"method":"eth_gasPrice","params":[]}' --compressed
{"jsonrpc":"2.0","id":6,"result":"0xee6b2800"}

I'll do some digging on the Sec-Fetch-Mode: cors header and see if that's something we can respond to.

ryanschneider commented 4 years ago

What I determine is how to get Chrome to copy the actual CORS preflight request as a CURL command, that would be the easiest thing to debug, I'll keep digging though.

ryanschneider commented 4 years ago

Ok, this makes even less sense. If I copy the request as a fetch, I get back:

fetch("https://mainnet.infura.io/v3/03a06ac61e0740ed8934ca4bc13c8cf1", {"credentials":"omit","headers":{"content-type":"application/json","sec-fetch-mode":"cors"},"referrer":"https://elevato.uk/payments/zamowienie/order-received/506/?key=wc_order_YZH3EpPwfdmKd","referrerPolicy":"no-referrer-when-downgrade","body":"{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"eth_gasPrice\",\"params\":[]}","method":"POST","mode":"cors"});

Notice that the fetch says "credentials":"omit". This might actually be a Chrome bug.

edit: According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials

When a request's credentials mode (Request.credentials) is "include", browsers will only expose the response to frontend JavaScript code if the Access-Control-Allow-Credentials value is true.

So this really does feel like a Chrome bug, since the fetch is set to omit. I'll see if I can find any open Chrome issues, maybe they will lead us to a work-around.

ryanschneider commented 4 years ago

Also, FWIW, confirmed I get the same issue on Brave (Version 0.67.123 Chromium: 76.0.3809.87 (Official Build)) (which is based on Chromium in recent releases), but not in Firefox 65.0b11 (which behaves like Safari).

olegabr commented 4 years ago

Nice research. My two cents:

epg.web3.version
{api: "0.20.7", getNode: ƒ, getNetwork: ƒ, …}
epg.web3metamask.version
{api: "0.20.7", getNode: ƒ, getNetwork: ƒ, …}

The 0.20.7 version of web3js is used. It cames from MetaMask. I see it as an inpage.js in debugger.

This is the place where withCredentials is set to true:

https://github.com/ethereum/web3.js/blame/0.20.7/lib/web3/httpprovider.js#L66

HttpProvider.prototype.prepareRequest = function (async) {
  var request;

  if (async) {
    request = new XHR2();
    request.timeout = this.timeout;
  } else {
    request = new XMLHttpRequest();
  }
  request.withCredentials = true;

  request.open('POST', this.host, async);
  if (this.user && this.password) {
    var auth = 'Basic ' + new Buffer(this.user + ':' + this.password).toString('base64');
    request.setRequestHeader('Authorization', auth);
  } request.setRequestHeader('Content-Type', 'application/json');
  if(this.headers) {
      this.headers.forEach(function(header) {
          request.setRequestHeader(header.name, header.value);
      });
  }
  return request;
};

This flag was added to fix this issue: https://github.com/ethereum/web3.js/issues/1665

I do not use filters in my code, so, I can try to exploit the HttpProvider.prototype.prepareRequest method to workaround this issue. However, I want to hear your ideas here.

olegabr commented 4 years ago

With this patch I've worked around the original error:

function epg_web3_prepareRequest_shim(async) {
  var request;

//  if (async) {
//    request = new XHR2();
//    request.timeout = this.timeout;
//  } else {
    request = new XMLHttpRequest();
//  }
//  request.withCredentials = true;

  request.open('POST', this.host, async);
  if (this.user && this.password) {
    var auth = 'Basic ' + new Buffer(this.user + ':' + this.password).toString('base64');
    request.setRequestHeader('Authorization', auth);
  } request.setRequestHeader('Content-Type', 'application/json');
  if(this.headers) {
      this.headers.forEach(function(header) {
          request.setRequestHeader(header.name, header.value);
      });
  }
  return request;
};
window.epg.web3.currentProvider.prepareRequest = epg_web3_prepareRequest_shim;

But regardless if I apply this patch or not, if I try to send tx via MetaMask, I get this error now:

inpage.js:1 MetaMask - RPC Error: Internal JSON-RPC error. {code: -32603, message: "Internal JSON-RPC error.", data: {…}, stack: "Error: Error: [ethjs-query] while formatting outpu…eaoehlefnkodbefgpgknn/background.js:1:1230603"}}'"}
ether-and-erc20-tokens-woocommerce-payment-gateway.js?ver=2.6.6:774 {code: -32603, message: "Internal JSON-RPC error.", data: {…}, stack: "Error: Error: [ethjs-query] while formatting outpu…eaoehlefnkodbefgpgknn/background.js:1:1230603"}}'"}
ether-and-erc20-tokens-woocommerce-payment-gateway.js?ver=2.6.6:805 {code: -32603, message: "Internal JSON-RPC error.", data: {…}, stack: "Error: Error: [ethjs-query] while formatting outpu…eaoehlefnkodbefgpgknn/background.js:1:1230603"}}'"}

Full error message:

"Error: Error: [ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"message":"Internal JSON-RPC error.","data":{"originalError":{}},"stack":"Error: BlockReEmitMiddleware - retries exhausted\n    at chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background.js:1:1230698\n    at async chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/background.js:1:1230603"}}'"
olegabr commented 4 years ago

Any update?

ryanschneider commented 4 years ago

Any idea where this.user and this.password are being set? I think that's the issue: you should not be using Basic auth at all when accessing Infura from a client-side app, auth should only be use when you want to lock a projectID to require it's project secret, which can't be done securely client-side, so should only be used in server-side applications.

My guess is that the user/pass being set are not valid for Infura, and we're returning a 400 error, and MM is retrying, leading to MM emitting the JSONRPC error you included, that error looks to be generated inside MM.