Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cordova doesn't send origin on request in some Android devices

This is a problem that happens on some android devices.

I have a Samsung Galaxy A5 (2017) with Google Chrome version 76.0.3809.89 and Android version 8.0.0.

When I deploy my Cordova application for the first time on this device and making a POST request I receive an error about CORS:

Access to XMLHttpRequest at 'http://foo.com/bar' from origin 'file://' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I can saw from Chrome's dev tool network tab that this POST request, doesn't have the origin parameter.

If I close the application from the task manager and starting it again I can make this request successfully and chrome sends the 'origin' parameter on request.

I can re-produce the problem again if I delete applications' data from device's settings.

I'm totally sure that this is not a problem with Content-Security-Policy or cordova-whitelist-plugin configuration because as I mention before, on some other android devices the application works just fine.

Something other that I need to mention is that with crosswalk plugin I didn't have this problem.

like image 309
yannisalexiou Avatar asked Aug 02 '19 12:08

yannisalexiou


2 Answers

This is an open bug on Chromium webview interesting the 76 version; in this specific version the OOR-CORS remains enabled in the first run.

https://bugs.chromium.org/p/chromium/issues/detail?id=991107#c14

A change list will be merged to the 76 branch directly.

like image 105
systempuntoout Avatar answered Oct 16 '22 12:10

systempuntoout


We're experiencing the same issue. We first tried to implement a workaround where the App detects the Chrome version. The first time it sees version 76 it automatically restarts the app. Most users wouldn't even notice the restart. The workaround was based on several reports which stated, that the problem would only appear during the first run of the app after Chroe 76 had been installed and permanently disappear after a restart. However, it turned out, that the problem often returns seemingly randomly, so the woraround wasn't sufficient.

Luckily we're in control of the server side too, so we implemented a workaround there. Basicly we simply accept the file:// origin. The security impact of this workaround certainly depends on the individual application, so think about it carefully in case you consider implemeinting something similar.

In our case we're using ASP.NET where we were facing an additional challenge. Unfortunately EnableCorsAttribute fails when specifying file:// as an origin, which is in line with the CORS specification, which requires a hostname to be present in each origin (except when specifying *). So we finally came up with the following solution (specific to ASP.NET of course):

Create an ICorsPolicyProvider that overrides the validation logic in case of origin file:// and delegates to a nested provider (the original one) in all other cases:

    /// <summary>
    /// Object that allows clients with `file://` origin. This is used as a workaround
    /// for issues on Android devices running Chrome 76. See
    /// https://bugs.chromium.org/p/chromium/issues/detail?id=991107#c14
    /// 
    /// Simply adding `file://` to the allowed origins list doesn't work, because by
    /// specification a hostname is required.
    /// 
    /// This workaround should be removed as soon as Chrome 76 distribution has dropped
    /// sufficiently.
    /// </summary>
    // TODO: Remove this workaround once Chrome 76 is not around anymore.
    public class CorsPolicyProviderAcceptingFileOrigin : ICorsPolicyProvider
    {
        public readonly ICorsPolicyProvider inner;

        public CorsPolicyProviderAcceptingFileOrigin(ICorsPolicyProvider inner)
        {
            this.inner = inner;
        }

        public async Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (
                request.Headers.TryGetValues("origin", out IEnumerable<string> origins)
                && origins.SequenceEqual(new[] { "file://" })
                )
            {
                var policy = new CorsPolicy()
                {
                    AllowAnyHeader = true,
                    AllowAnyMethod = true,
                    AllowAnyOrigin = false,
                };
                policy.Origins.Add("file://");

                return policy;
            }

            return await this.inner.GetCorsPolicyAsync(request, cancellationToken);
        }
    }

and then use it like this in WebApiConfig.cs:

// get the original EnableCorsAttribute
ICorsPolicyProvider cors = ...;

// this is a workaround for Android devices running Chrome 76 and should be removed in future versions.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=991107
cors = new CorsPolicyProviderAcceptingFileOrigin(cors);

config.EnableCors(cors);
like image 3
MarkusM Avatar answered Oct 16 '22 14:10

MarkusM