Closed lardyNiji closed 3 years ago
Content-Security-Policy
is a different security mechanism than CORS (Cross-Origin Resource Sharing).
In cordova-android@10, we implemented something called a WebAssetLoader, which proxies requests through https://localhost
protocol. The WebAssetLoader kind of acts like a private web server only accessible to your app. This was done because some web view features requires you to be on a "secure context" (e.g https
) for the features to be enabled. In doing so, it does enable the CORS enforcement.
Cordova android 9.x uses the plain old file system (file://
) which didn't enforced CORs. This is why you see the XHR request work in 9.x, but not in 10.x. You can make 10.x behave like 9.x by enabling the AndroidInsecureFileModeEnabled
preference:
<preference name="AndroidInsecureFileModeEnabled" value="true" />
CORS is a security mechanism for CORS-enabled browsers that are controlled by the backend server. So in this case, https://google.com
must provide the required response headers for the browser to accept the request response. They do not provide the Access-Control-Allow-Origin: https://localhost
or Access-Control-Allow-Origin: *
response header, therefore the request is rejected by the browser / webview.
There is no API available in the webview to disable CORS. Assuming you don't have access to https://google.com
to make the appropriate backend change, the only workaround at this point is to not use the browser's request mechanism (neither fetch()
or XMLHttpRequest
) and instead find/build a cordova plugin that does a native request, which is not bounded by CORS.
Another approach is to configure a proxy server that is configured to use the CORS protocol in which your app can make request to, which will be redirected to https://google.com
, then you can relay the response back to the client. This approach will still allow you to use the browser's HTTP request APIs.
Now that we got all that information out there... May I ask more details on your use case?
First of all thank you for the quick and detailled answer.
We are trying to update cordova 9 existing apps which embend some xhr calls to an api and some other resources.
These apps are internal apps not delivered on Google Play store then i think that we are going to use the workaround with cordova preferences you have explained in your first point.
I've tested it with my hellocdv10 demo app and this configuration do the job.
Some background knowledge...
Content-Security-Policy
is a different security mechanism than CORS (Cross-Origin Resource Sharing).In cordova-android@10, we implemented something called a WebAssetLoader, which proxies requests through
https://localhost
protocol. The WebAssetLoader kind of acts like a private web server only accessible to your app. This was done because some web view features requires you to be on a "secure context" (e.ghttps
) for the features to be enabled. In doing so, it does enable the CORS enforcement.Cordova android 9.x uses the plain old file system (
file://
) which didn't enforced CORs. This is why you see the XHR request work in 9.x, but not in 10.x. You can make 10.x behave like 9.x by enabling theAndroidInsecureFileModeEnabled
preference:
<preference name="AndroidInsecureFileModeEnabled" value="true" />
But let's assume you don't want to use this workaround
CORS is a security mechanism for CORS-enabled browsers that are controlled by the backend server. So in this case,
https://google.com
must provide the required response headers for the browser to accept the request response. They do not provide theAccess-Control-Allow-Origin: https://localhost
orAccess-Control-Allow-Origin: *
response header, therefore the request is rejected by the browser / webview.There is no API available in the webview to disable CORS. Assuming you don't have access to
https://google.com
to make the appropriate backend change, the only workaround at this point is to not use the browser's request mechanism (neitherfetch()
orXMLHttpRequest
) and instead find/build a cordova plugin that does a native request, which is not bounded by CORS.Another approach is to configure a proxy server that is configured to use the CORS protocol in which your app can make request to, which will be redirected to
https://google.com
, then you can relay the response back to the client. This approach will still allow you to use the browser's HTTP request APIs.Now that we got all that information out there... May I ask more details on your use case?
This maybe the most detailed answer I have ever read , thank you!
hello after so much banging on this problem finally one that makes me understand everything. This:
How do I understand it makes you emulate the device like it's a version 9?
Go to the config.xml file and at what point?
Why did I do this:
<platform name = "android">
<preference name = "AndroidInsecureFileModeEnabled" value = "true" />
<icon src = "res / android / icon.png" />
<allow-intent href = "market: *" />
<resource-file src = "resources / android / xml / network_security_config.xml" target = "app / src / main / res / xml / network_security_config.xml" />
<access origin = "*" />
</platform>
But it still doesn't work in me the state has always been 0
Some background knowledge...
Content-Security-Policy
is a different security mechanism than CORS (Cross-Origin Resource Sharing).In cordova-android@10, we implemented something called a WebAssetLoader, which proxies requests through
https://localhost
protocol. The WebAssetLoader kind of acts like a private web server only accessible to your app. This was done because some web view features requires you to be on a "secure context" (e.ghttps
) for the features to be enabled. In doing so, it does enable the CORS enforcement.Cordova android 9.x uses the plain old file system (
file://
) which didn't enforced CORs. This is why you see the XHR request work in 9.x, but not in 10.x. You can make 10.x behave like 9.x by enabling theAndroidInsecureFileModeEnabled
preference:
<preference name="AndroidInsecureFileModeEnabled" value="true" />
But let's assume you don't want to use this workaround
CORS is a security mechanism for CORS-enabled browsers that are controlled by the backend server. So in this case,
https://google.com
must provide the required response headers for the browser to accept the request response. They do not provide theAccess-Control-Allow-Origin: https://localhost
orAccess-Control-Allow-Origin: *
response header, therefore the request is rejected by the browser / webview.There is no API available in the webview to disable CORS. Assuming you don't have access to
https://google.com
to make the appropriate backend change, the only workaround at this point is to not use the browser's request mechanism (neitherfetch()
orXMLHttpRequest
) and instead find/build a cordova plugin that does a native request, which is not bounded by CORS.Another approach is to configure a proxy server that is configured to use the CORS protocol in which your app can make request to, which will be redirected to
https://google.com
, then you can relay the response back to the client. This approach will still allow you to use the browser's HTTP request APIs.Now that we got all that information out there... May I ask more details on your use case?
Is there a way to change what it sends as the source? We normally use appname.companyname.com in our URLs.
How do I understand it makes you emulate the device like it's a version 9?
CORS is a browser feature, so the feature isn't tied to Android versions. It depends on the Android Webview version that happens to be running. However I'm not sure when exactly CORS started being enforced in the Android Webview.
iOS is slightly different in that their system webview is tied to the OS, and in particular they started enforcing CORS with their WKWebView available in iOS 9. Android Webview is an upgradeable package, independent from the OS so you can still have an Android 9 device (or an app running cordova-android@9) and still encounter CORS issues.
Is there a way to change what it sends as the source? We normally use appname.companyname.com in our URLs.
If by source, you mean the origin value, you have limited control over it. The origin is the scheme and domain of the document, or null
if there is no domain (such as when using the filesystem file://
protocol).
Starting on cordova-android@10 we have support for scheme handlers allowing you to change the scheme of the app, instead of using the file://
approach as we've had in the past. The purpose of this is actually heavily influenced by CORS as all filesystem based activity are considered cross-origin by default, whereas doing an XHR request against your own scheme will have relaxed CORS rules since it will be considered part of the same region.
For android, the default scheme if enabled is https://localhost
if I recall correctly, but you can change it using the following preferences:
<preference name="scheme" value="https" /> <!-- This requires cordova-android@10.1 or later -->
<preference name="hostname" value="localhost" />
Note that for Android, the scheme must be either https
or http
, but the hostname can be any valid domain-like value. Do note that the scheme system operates kind of like an interception, so if you choose the same scheme as a real server, that server will not be reachable as requests will simply get directed to the scheme system instead of the actual network. So you should choose something that is unique for your app, that won't conflict with real servers. Learn More
So by changing the scheme settings, you can influence the origin value. Don't forget that web storage features like local storage are tied to origins, each origin have their own database so by changing the scheme settings/origin, you will lose access to previously stored web storage data.
How do I understand it makes you emulate the device like it's a version 9?
CORS is a browser feature, so the feature isn't tied to Android versions. It depends on the Android Webview version that happens to be running. However I'm not sure when exactly CORS started being enforced in the Android Webview.
iOS is slightly different in that their system webview is tied to the OS, and in particular they started enforcing CORS with their WKWebView available in iOS 9. Android Webview is an upgradeable package, independent from the OS so you can still have an Android 9 device (or an app running cordova-android@9) and still encounter CORS issues.
Is there a way to change what it sends as the source? We normally use appname.companyname.com in our URLs.
If by source, you mean the origin value, you have limited control over it. The origin is the scheme and domain of the document, or
null
if there is no domain (such as when using the filesystemfile://
protocol).Starting on cordova-android@10 we have support for scheme handlers allowing you to change the scheme of the app, instead of using the
file://
approach as we've had in the past. The purpose of this is actually heavily influenced by CORS as all filesystem based activity are considered cross-origin by default, whereas doing an XHR request against your own scheme will have relaxed CORS rules since it will be considered part of the same region.For android, the default scheme if enabled is
https://localhost
if I recall correctly, but you can change it using the following preferences:<preference name="scheme" value="https" /> <!-- This requires cordova-android@10.1 or later --> <preference name="hostname" value="localhost" />
Note that for Android, the scheme must be either
https
orhttp
, but the hostname can be any valid domain-like value. Do note that the scheme system operates kind of like an interception, so if you choose the same scheme as a real server, that server will not be reachable as requests will simply get directed to the scheme system instead of the actual network. So you should choose something that is unique for your app, that won't conflict with real servers. Learn MoreSo by changing the scheme settings, you can influence the origin value. Don't forget that web storage features like local storage are tied to origins, each origin have their own database so by changing the scheme settings/origin, you will lose access to previously stored web storage data.
Awesome, thank you very much
I am commenting here due to Android 12 Behaviour changes.
@breautek In the above reply, you said "if you choose the same scheme as a real server, that server will not be reachable as requests will simply get directed" If suppose my remote server is https://abc.com
should I not use "scheme" value as 'https'? and the value should be some like below?
<preference name="scheme" value="httpsapps" />
Can you give me an example? I am getting the below error when I give scheme
as https
and hostname
as abc.com
(remote server domain name) "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Orgin'
On Android, scheme
must be either http
or https
. Android doesn't accept any other value for the scheme.
I'm not sure what happens if you're real server is https://abc.com
and you set to scheme
/ hostname
to http://abc.com
, but I think it will work, as long as you don't try to hit http://abc.com
and expect to make a request to your real server. In most cases leaving the defaults should be sufficient though. Using https://localhost
is guaranteed not to conflict with any real servers. It is up to the server however to read the Origin
request header and set the Access-Control
response headers appropriately.
Initially, I had the default config https://localhost
now I updated the android target SDK to 31. After this, I started getting issues. My REST API returns a cookie and tries to set it. But it throws the error
"This set-cookie header didn't specify a samesite attribute and was defaulted to samesite=lax
and was blocked because it came from a cross-site response which was not the response to top-level navigation. The set-cookie had to have been set with "SameSite=None
" to enable cross-site usage.
Note: I cannot change the cookie response on the server side. It's an ionic mobile app using Cordova.
To overcome the above issue, I tried to configure a custom scheme
and hostname
equal to my real server. After which I started getting "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin".
Initially, I had the default config
https://localhost
now I updated the android target SDK to 31. After this, I started getting issues. My REST API returns a cookie and tries to set it. But it throws the error "This set-cookie header didn't specify a samesite attribute and was defaulted tosamesite=lax
and was blocked because it came from a cross-site response which was not the response to top-level navigation. The set-cookie had to have been set with "SameSite=None
" to enable cross-site usage.Note: I cannot change the cookie response on the server side. It's an ionic mobile app using Cordova.
To overcome the above issue, I tried to configure a custom
scheme
andhostname
equal to my real server. After which I started getting "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin".
Wish I could help but I started having this problem a year or so ago and had to switch to the plugin cordova-plugin-advanced-http to bypass cookie security while we worked to remove all cookie authentication. All our problems are gone now that we switched to token auth. Cookies are a dead tech for use as logins. You can do the same, use that plugin to keep things going while you replace your cookies. https://learn.g2.com/cookieless-future
Note: I cannot change the cookie response on the server side. It's an ionic mobile app using Cordova.
Cookie policies, like the Access-Control CORS headers, are set by the server. If yo do not have access to the server side to allow cross origin cookies, then you effectively don't have permission to communicate with that server and that webserver only supports standard browsers that connect to the webserver directly.
To overcome the above issue, I tried to configure a custom scheme and hostname equal to my real server. After which I started getting "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin".
This is wrong, because by setting your scheme scheme/hostname you won't actually hit your real server. The request gets intercepted by something called WebViewAssetLoader to load local files. It sounds like you're using a POST
or some other HTTP method other than GET
which triggers preflight requests which is why you see a CORS issue here.
Cookies are intended to be set to it's own origin. It's a potential security risk to expose cookies cross origin, but with server configuration access, you can set the samesite
to None
, allowing cookies to be sent cross-origin. Imo, cookies is a dated technology that worked well in the past when your only client was a web browser hitting a webserver directly (that is the web browser is being served content directly from the server). While cordova runs in a webview, you generally don't load the document itself from a remote server. You need to have the app support offline where the server may not be reachable. So you bundle web assets with the mobile app itself, which makes the app cross-origin. EThis is what makes cookie-based authentication a poor choice for an authentication mechanism. It works ok if your only client is a standard web browser, but if you intend to support multiple different kinds of clients, cookie-based authentication tends to falls apart, especially now that browser vendors are locking down cookies to improve on security and privacy issues.
This thread is getting off topic so in order to respect the OP's inbox, I'm going to lock it here. If you have further questions on this matter, I'd suggest asking our Slack community. If you believe you've found a bug, then feel free to create a new issue.
Edit: For those who have access to the server to edit cookie policies, you might be able to explicitly set your Domain
value to of the cookie to the base domain. For example, Set-Cookie <cookie>; Domain=example.com
Then have your app scheme be set to a subdomain of that domain, e.g. https://myapp.example.com
.
MDN states
Multiple host/domain values are not allowed, but if a domain is specified, then subdomains are always included.
And they also state:
A cookie is associated with a particular domain and scheme (such as http or https), and may also be associated with subdomains if the Set-Cookie Domain attribute is set. If the cookie domain and scheme match the current page, the cookie is considered to be from the same site as the page, and is referred to as a first-party cookie.
So this leads me to believe that it is possible to configure the server and the app in a way that the app will treat cookies as first-party cookies, but this needs to be tested and this still requires server side configurations.
Bug Report
Problem
Simple GET xhr request (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) in cordova-android@^10.0.0 trigger CORS
What is expected to happen?
Simple xhr GET request should not trigger CORS
What does actually happen?
Simple xhr GET request should trigger CORS
Example: Access to XMLHttpRequest at 'https://www.google.com/' from origin 'https://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Information
I have tested with two Cordova applications out of the box :
Result in Chrome console :
Result in Chrome console :
Command or Code
I've created two Cordova applications
Cordova 9.x
Cordova 10.x
File changes for both applications
Environment, Platform, Device
Device
Version information
First app
Second App
Cordova Cli: 10.0.0
Checklist