Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phonegap Android app ajax requests to HTTPS fail with status 0

Ajax HTTPS requests from my PhoneGap/Cordova app on Android inexplicably fail with status=0. It appears only when signing the app with the release key (i.e., exporting from ADT), but doesn't appear when signing with debug key (running directly in emulator or phone).

request = new XMLHttpRequest()
request.open "GET", "https://some.domain/", true
request.onreadystatechange = ->
  console.log "** state = " + request.readyState
  if request.readyState is 4
      console.log "** status = " + request.status

request.send()

always outputs

** state = 4
** status = 0

It doesn't matter if i install the app from Play Store or with adb utility. I presume it could be connected with the certificate, since not all HTTPS domains fail this way.

like image 902
Ernests Karlsons Avatar asked Nov 17 '13 21:11

Ernests Karlsons


2 Answers

I was having the same problem but my solution was a little different.

  1. In only the Android App build of my Cordova app, AJAX calls to my server via HTTPS were being blocked. Not in iOS, not in desktop browsers. Most confusingly, in the actual Android Browser the HTTPS AJAX calls would work no problem.

  2. I verified that I could make HTTPS AJAX calls to well known and trusted URLs such as https://google.com as well as regular HTTP calls to any URL I cared to try.

  3. This led me to believe that my SSL cert was either NOT installed 100% correctly OR the cheap (~$10 usd) cert from PositveSSL was not universally trusted OR both.

  4. My cert was installed on my AWS Load Balancer so I looked around about how I may have messed this up and also how PositiveSSL was not the best cert to be using in terms of trustworthiness. Lucky me found an article covering AWS ELB installation of certs AND they happened to be using a PositiveSSL cert! Contained within was this little gem:

"...Don’t be fooled by the AWS dialog, the certificate chain isn’t really optional when your ELB is talking directly to a browser..."

http://www.nczonline.net/blog/2012/08/15/setting-up-ssl-on-an-amazon-elastic-load-balancer/

Drumroll....

I reinstalled the cert with the "optional" Certificate Chain info and voilà!, the HTTPS AJAX calls to my server started working.

So it appears that the Android Webview is more conservative than the Android Browser in terms of cert trust. This is not totally intuitive since they are supposed to be basically the same tech.

like image 165
SoldierOfFortran Avatar answered Jan 15 '23 04:01

SoldierOfFortran


It happens when the requested URL responds with an erroneous or self-signed certificate. While testing or distributing the app to friends, setting <application android:debuggable="true"...> in AndroidManifest.xml is enough — it automatically bypasses certificate errors.

But Google Play Store will not accept an APK with android:debuggable="true". First of all, the certificates, of course, need to be fixed. But while that happens, here is a workaround for PhoneGap/Cordova 3:

  1. In your app package create a subclass for CordovaWebViewClient:

    public class SSLAcceptingCordovaWebViewClient extends CordovaWebViewClient {
        public SSLAcceptingCordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
            super(cordova, view);
        }
    
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();
        }
    }
    
  2. Same for IceCreamCordovaWebViewClient:

    public class SSLAcceptingIceCreamCordovaWebViewClient extends IceCreamCordovaWebViewClient {
        public SSLAcceptingIceCreamCordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
            super(cordova, view);
        }
    
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            handler.proceed();
        }
    }
    
  3. in <Your App Name>.java add an override for makeWebViewClient:

    @Override
    protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
        if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
            return new SSLAcceptingCordovaWebViewClient(this, webView);
        } else {
            return new SSLAcceptingIceCreamCordovaWebViewClient(this, webView);
        }
    }
    

Et voilà! SSL errors will be disregarded. However, never use erroneous certificates. Try to fix them first and use this dirty workaround only when you run out of other solutions.

like image 37
Ernests Karlsons Avatar answered Jan 15 '23 04:01

Ernests Karlsons