Secure communication with certificate pinning in a Cordova app

By Mike Roberts

If your app talks to some sort of backend to manage user authentication, store user data, etc, then you need to be using SSL and HTTPS for those requests. If you’re not, anyone sitting on the same network as your user can see and read that communication as if your user was just saying their password out loud.

But how do you make sure your HTTPS connections are secure? Even when using HTTPS connections, a man in the middle attack can sit on a proxy between your user and your server and decrypt, read, and re-encrypt the traffic to pass it on to your server. Certificate pinning to the rescue!

Certificate pinning makes sure that the SSL certificate matches a certificate in your app before establishing the connection. If the destination doesn’t match the certificate you’ve specified then the request is canceled; it can’t be intercepted and spied upon.

Installing the Plugin

Cordova doesn’t natively support certificate pinning so we’ll need to use a plugin to manage that. For this walkthrough, we’ll be using https://github.com/silkimen/cordova-plugin-advanced-http but there are many others to choose from. First we need to install the plugin:

$ cordova install cordova-plugin-advanced-http

// Or if you're using Ionic you can do

$ ionic cordova install cordova-plugin-advanced-http

Certificate Files

Next, you need to get your certificate (.cer) files. I'm not going to go into that here because it's a long discussion that could have its own set of blog posts. For now, I'll point you to a couple of articles –https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e and https://www.owasp.org/index.php/Pinning_Cheat_Sheet.

Once you have your certificate files, you’ll want to place them somewhere in your app. I usually make a certs folder in the root of the cordova project so that’s what I’ll use for the examples. In your config.xml file you’ll need to specify your certs as resource files like so:

<!-- if you're using cordova-android < 7> -->
<platorm name="android">
  <resource-file src="certs/cert1.cer" target="assets/cert1.cer" />
  <resource-file src="certs/cert2.cer" target="assets/cert2.cer" />
</platform>

<!-- if you're using cordova-android >= 7 -->
<platorm name="android">
  <resource-file src="certs/cert1.cer" target="app/src/main/assets/cert1.cer" />
  <resource-file src="certs/cert2.cer" target="app/src/main/assets/cert2.cer" />
</platform>


<platorm name="ios">
  <resource-file src="certs/cert1.cer" />
  <resource-file src="certs/cert2.cer" />
</platform>

Regular Javascript Example

Now to enable the pinning we'll follow the instructions in the plugins docs. If you're using Ionic skip to the next section, this is next bit is for a regular Cordova app. In whichever main javascript file you use that runs when your app launches, put this bit of code
cordova.plugin.http.enableSSLPinning(true, function() {
  // Congratulations, you have set up SSL pinning.
}, function() {
  // Boooooo, SSL pinning failed.
});

Now you use this plugin to make all your requests:

cordova.plugin.http.get('https://your.url.here')
  .then((data) => {
    // Success, check your data.
  })
  .catch((error) => {
    // Uh oh, something went wrong. Check your error.
  })

Ionic App Example

If you’re using Ionic you can install the Ionic Native package for the http plugin:

$ npm install @ionic-native/http

Then all you need to do is enable ssl pinning in your app.component.ts file and use the HTTP package from ionic-native to make all your requests.

// app.component.ts
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { HTTP } from '@ionic-native/http';

@Component()
export class App {
  constructor(
    private platform: Platform,
    private http: HTTP
  ) {
    this.platform.ready()
      .then(() => {
        this.http.enableSSLPinning(true)
          .then(() => {
            // Congratulaions, you have set up SSL Pinning.
          })
          .catch(() => {
            // Boooooo, SSL pinning failed.
          });
      })
  }

  getStuff() {
    this.http.get('https://your.url.here')
      .then((data) => {
        // Success, check your data.
      }).catch((error) => {
        // Uh oh, something went wrong. Check your error.
      });
  }
}

That's it!

Now you have enabled SSL pinning with the Cordova plugin, have fun knowing that your requests are more secure now. If you have a product where security is important, there’s really no reason not to use this, especially if you’re communicating with your own API, it makes it a lot harder for your requests to be intercepted and read.