Allowing mixed content on Android Cordova app

I am building an Android app using Cordova, that runs on Android 8.1. This app will be used in a LAN only and it needs to consume data from a local REST Webserver over HTTP… but because Android 8 forces Ionic apps to run under HTTPS, I am getting an error when trying to access the REST server over HTTP.

By doing some research, I discovered that the solution is to change WebView settings to allow mixed content. There were several ways to do this, but the easiest one was to add this line to my Ionic’s config.xml:

<preference name="MixedContentMode" value="2" />

According to documentation, the posibles values for this setting would be:

0: MIXED_CONTENT_ALWAYS_ALLOW
1: MIXED_CONTENT_NEVER_ALLOW
2: MIXED_CONTENT_COMPATIBILITY_MODE

First time I tried the app after adding this line, everything worked perfectly, but the next time I compiled and deployed the app, I got the same error again:

Mixed Content: The page at ‘https://localhost/config’ was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint ‘http://192.168.0.5:5656/’. This content should also be served over HTTPS.

Anyone know how to permanently solve this?

My versions are:

Cordova: 10.0.0
Angular: 11.0.7
Ionic: 6.12.4

PS #1: For compatibility reasons with the devices where the app should run, I HAVE to use these versions, so updating any of them is out of the question.

PS #2: Since this app will run in LAN exclusively, my customer don’t want to deal with the complications of moving the REST webservice to HTTPS, so that’s is out of the question too.

Well, it seems that Android 8.1 still allows me to serve my app using HTTP instead of HTTPS. I changed the scheme on my config.xml:

<preference name="Scheme" value="http" />

And the app runs normally and now connects to my REST webservice without problems. Anyway, I would still like to know how to solve the problem with the app needs to be served with HTTPS.

A new problem: after a first build that worked perfectly, the second build now fails with this error:

{"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":"http://192.168.0.5:5656","ok":false,"name":"HttpErrorResponse","message":"Http failure response for http://192.168.0.5:5656: 0 Unknown Error","error":{"isTrusted":true}}

My code:

  import { HttpClient, HttpHeaders } from '@angular/common/http';
  ...
  constructor(
    private http: HttpClient,
    private app: AppService,
    private ui: UIService
  ) { ... }
 ...
 this.httpOptions = {
      headers: new HttpHeaders({
        'Accept': 'application/json'
      })
    };
 ...
  haleServer(serverAddr:string) {
    return new Promise((resolve, reject) => {
      const payload = {
        "ttype" : this.requests.hale,
        "termid": this.app.termid,
        "sent": new Date(),
        "data": {}
      }
      this.http
          .post<any[]>(serverAddr, payload, this.httpOptions)
          .pipe(
            timeout(5000),
            catchError(e => throwError(e))
          )
          .subscribe(
            res => resolve(res),
            err => reject(err) 
          );        

    });
  } // haleServer

As far as I know that is a CORS error, but no CORS preflight request was sent to my server. After building 2 or 3 more times, then everything starts to work perfectly again… this is driving me crazy!!

Any ideas?

if you are using cordova-plugin-ionic-webview, mixed content is enabled by default as the default value is 0, MIXED_CONTENT_ALWAYS_ALLOW

I am using WebView plugin and the problem still persists.

Screen Shot 2021-05-03 at 14.32.02

What I mean is you shouldn’t add the
<preference name="MixedContentMode" value="2" />

That could cause issues instead of fixing them.

Ok, I will remove it to see if that makes any difference. I will let you know later today.

Well, I kind of solved this by instructing ionic to launch the app from http://localhost instead of https://localhost, but this is causing problems in Android 5 where the device refuse to load the app from a non-secured url. So, I am back to the mixed content problem.

I already removed the MixedContentMode preference from config.xml but I still can’t get the Angular HTTP client to communicate with my server. I also tried to install the Ionic Native HTTP client, but it causes all cordova plugins to not being loaded anymore, so its useless for me.

I even modified the file node_modules/cordova-android/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java to add this line in the initWebViewSettings method:

settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);

And that changed this message:

05-14 14:42:07.309 13750 13750 I chromium: [INFO:CONSOLE(1)] "Mixed Content: The page at 'https://localhost/config' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://192.168.0.5:5656/'. This request has been blocked; the content must be served over HTTPS.", source: https://localhost/polyfills-es2015.22f66b7f78f7020722f2.js (1)

for this:

05-14 14:57:30.888 14174 14174 I chromium: [INFO:CONSOLE(1)] "Mixed Content: The page at 'https://localhost/config' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://192.168.0.5:5656/'. This content should also be served over HTTPS.", source: https://localhost/polyfills-es2015.22f66b7f78f7020722f2.js (1)

but even the request is not being “blocked”, I still get this error from the Angular HTTP client:

05-14 14:57:30.908 14174 14252 E ETP : [config] {"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":"http://192.168.0.5:5656","ok":false,"name":"HttpErrorResponse","message":"Http failure response for http://192.168.0.5:5656: 0 Unknown Error","error":{"isTrusted":true}}

And I am 100% sure is not a CORS error because I wrote my server my self from scratch (so I manually handle the CORS preflight) and I not receiving any preflight request from the app.

My app is completed and its totally useless because this unbelievable problem !

Hi,

I’m facing the same issue :
Mixed Content: The page at ‘https://localhost/config’ was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint ‘http://192.168.0.5:5656/’. This content should also be served over HTTPS.

Did you fine any solutions ?

What finally worked for me was patching the Cordova java source code to force it to allow mixed content and removing that entry from the config.xml file.

The relevant entries in my config.xml at this moment:

    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <preference name="Scheme" value="https" />
    <preference name="usesCleartextTraffic" value="true" />

And the modified code for initiWebViewSettings method on the file node_modules/cordova-android/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java:

   @SuppressLint({"NewApi", "SetJavaScriptEnabled"})
    @SuppressWarnings("deprecation")
    private void initWebViewSettings() {
        webView.setInitialScale(0);
        webView.setVerticalScrollBarEnabled(false);
        // Enable JavaScript
        final WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);

        // ----------------- BEGIN OF CODE MODIFICATION ---------------------
        // VES APRIL 2021
        // REQUIRED TO ALLOW MIXED CONTENT
        settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 
        // ----------------- END OF CODE MODIFICATION ---------------------

        String manufacturer = android.os.Build.MANUFACTURER;
        LOG.d(TAG, "CordovaWebView is running on device made by: " + manufacturer);

        //We don't save any form data in the application
        settings.setSaveFormData(false);
        settings.setSavePassword(false);

        // Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
        // while we do this
        settings.setAllowUniversalAccessFromFileURLs(true);
        settings.setMediaPlaybackRequiresUserGesture(false);

        // Enable database
        // We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
        String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
        settings.setDatabaseEnabled(true);
        settings.setDatabasePath(databasePath);


        //Determine whether we're in debug or release mode, and turn on Debugging!
        ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
        if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
            enableRemoteDebugging();
        }

        settings.setGeolocationDatabasePath(databasePath);

        // Enable DOM storage
        settings.setDomStorageEnabled(true);

        // Enable built-in geolocation
        settings.setGeolocationEnabled(true);

        // Enable AppCache
        // Fix for CB-2282
        settings.setAppCacheMaxSize(5 * 1048576);
        settings.setAppCachePath(databasePath);
        settings.setAppCacheEnabled(true);

        // Fix for CB-1405
        // Google issue 4641
        String defaultUserAgent = settings.getUserAgentString();

        // Fix for CB-3360
        String overrideUserAgent = preferences.getString("OverrideUserAgent", null);
        if (overrideUserAgent != null) {
            settings.setUserAgentString(overrideUserAgent);
        } else {
            String appendUserAgent = preferences.getString("AppendUserAgent", null);
            if (appendUserAgent != null) {
                settings.setUserAgentString(defaultUserAgent + " " + appendUserAgent);
            }
        }
        // End CB-3360

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        if (this.receiver == null) {
            this.receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    settings.getUserAgentString();
                }
            };
            webView.getContext().registerReceiver(this.receiver, intentFilter);
        }
        // end CB-1405
    }

1 Like

Thanks vespinasgmailcom

// ----------------- BEGIN OF CODE MODIFICATION ---------------------
// VES APRIL 2021
// REQUIRED TO ALLOW MIXED CONTENT
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
// ----------------- END OF CODE MODIFICATION ---------------------

this did the trick