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

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.


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);