Web Security

Securing Ionic 4 Cordova Apps

March 5th, 2019 | By Karan Gandhi | 10 min read

A step-by-step tutorial to ensure your Ionic 4 Cordova app is secure, including source code protection, connection, and storage.

Ionic is a hybrid WebView-based framework. Recently, the Ionic Team released the Ionic 4. Traditionally, Ionic applications depend on the Cordova Framework to access Native APIs.

This article will cover some of the best security practices while developing Ionic Applications.

Analyzing Cordova Applications

Cordova applications are websites running on smartphone WebViews inside separate application containers. The applications’ IPA/APK files are simple archives that can be uncompressed. If we check the assets folder, we can see the bundled files, making the code easily readable.

Hypothetically, by transferring the code to a debug APK container, we will have access to code + inspect mode.

In essence, secure Cordova apps must be developed with the assumption that:

  • The code is visible to the naked eye;


The code can be debugged & local storage is accessible. As we’ll explore in the final section of this article, these weaknesses can be minimized with a JavaScript security solution such as Jscrambler.

With those assumptions in mind, we must explore the security strategies we can employ. We will divide the article into the following sections:

  • Securing App to Server connection

  • Preventing external code execution

  • Securing local storage

  • Advanced integrity checks

Securing App-to-Server Connection

A smartphone app communicates with a backend server via an API or web service. Traditionally, web services use an HTTP protocol. An HTTPS protocol adds a significant layer of security. Via HTTPS, the communication between the app and the server is encrypted.

Additionally, Apple introduced App Transfer Security (ATS) in iOS 9. ATS forces iOS Apps to connect over HTTPS protocol.

Similarly, Android 9 Pie blocks cleartext traffic(HTTP). Devices with Android Pie will throw an error while communicating over HTTP unless the app explicitly allows it in the Network Security configuration.

SSL Pinning

Users/Apps dependent on Certificate Authorities (CA) and Domain Name Servers (DNS) to validate domains for TLS. Unsafe certificates can be installed on a user device, opening the device for a Man-in-the-Middle attack.

With SSL Pinning, certificates can be bundled inside an application. In Cordova, we can implement certificate-based pinning via the advanced HTTP plugin.

Preventing External Code Execution

Cordova apps are websites running in local WebView containers.

As such, they are susceptible to XSS vulnerabilities. To prevent such execution, the application can be secured by using the Cordova Whitelist Plugin and a strict CSP.

Cordova Whitelist Plugin and Content Security Policy

The whitelist plugin allows us to define the security of an app via params in confix.xml. We can allow/block external navigation, control network access and restrict application intents.

The access tag allows us to restrict domain access in an app. By default, everything is allowed:

  <access origin="*" />


We can allowlist domains by replacing the * with individual domains. let's assume we have to restrict traffic to trusteddomain.com. The access tag doesn't allow allowlisting of WebSockets.

   <access origin="https://trusteddomain.com" />
    <access origin="https://api.trusteddomain.com" />


The allow-intent tag allows us to restrict calls to external apps via hyperlinks. By default, hyperlinks to a web browser, phone, SMS, email, and map intent are enabled.

     <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />


Another way of enforcing an allowlist is by adding a Content Security Policy (CSP) tag to the index.html page. CSP is a security standard used to prevent XSS attacks. You can read more about CSP. Here is a sample tag:

 <meta http-equiv="Content-Security-Policy" 
    content="default-src 'self' https://trusteddomain.com data: gap: https://ssl.gstatic.com; 
    style-src 'self' 'unsafe-inline'; 
    media-src *;
    >


The value gap: is required by iOS apps and https://ssl.gstatic.com is required by Android for Talkback. Other than that, most resources are allowlisted to trusteddomain.com. Furthermore, we can restrict the WebSockets and XHR using the connect-src attribute. Right now, it's implicitly blocked via the default-src.

Securing Local Storage

Local storage and the SQLite plugin are common methods to store persistent data in a Cordova app. Additionally, Ionic has a storage binding, which allows us to use IndexedDB, SQLite, and local storage in an Ionic app using a single method.

However, data in IndexedDB and local storage can be easily viewed in a debug app via browser debugging tools. Once retrieved, SQLite databases can be viewed via database viewers. We can secure data using two plugins in Cordova.

Secure Storage


The secure storage plugin allows us to store key-value data securely. In iOS, data is stored in Keychain via the SAMS keychain library. On Android, a random AES key encrypts the data. The encrypted AES key is then stored in SharedPreferences.

SQL Cipher


In some scenarios, we require SQL Tables in our app. In this case, we can use SQLCipher to secure the database.

SQL Cipher is a drop-in plugin for the SQLite cipher library. Data in SQLCipher is encrypted via 256-bit AES-like Secure storage.

The database is not readable without a key. However, the SQL key can be retrieved easily by unzipping the APK/IPA. As a precaution, it's advisable to generate a unique key for a user at the backend server. The generated key can be retrieved from the backend when the database needs to be opened.

Alternatively, the key can be stored in secure storage.

Advanced Integrity Checks

These are individual checks which can be performed on an app. Root checks are used to determine whether a device is rooted. SafetyNet Attestation is used to check the Device and APK Integrity.

Jscrambler provides Code Locking, Self-Defending, and Obfuscation, which act as a JavaScript integrity check.

Root and Jailbreak checks


Rooted, Jailbroken, or devices running custom ROMs should be considered insecure. Generally, custom ROMs allow users to use better and newer firmware on Android. However, a rooted device has read/write access to the /data and /system folders.

This allows a user or an app to bypass native app security restrictions. It's advisable to run a root check while performing a critical functionality.

The iRoot plugin has Jailbreak and Root checks. For Android users, you can also check the RootBeer Plugin. It's a drop-in wrapper for the popular rootbeer library.

SafetyNet

SafetyNet is an Android-specific library to attest to APK and device Integrity. This functionality is only available on Play Certified devices or devices with PlayServices installed.

The Attestation API is a rate-limited function that provides a spot check for the device and APK. VerifyApps provides functions for detecting harmful apps and enabling the Verify Apps feature.

Here is the plugin for the SafetyNet API.

Code Obfuscation and Protection (Jscrambler)

As mentioned earlier, the code of Cordova applications can be easily retrieved. As so, it enables attackers to copy (re-distribute) the code, reverse-engineer sensitive logic, or even tamper with the code to abuse the app (such as unlocking features or violating license agreements).

This security weakness is best minimized with a JavaScript Application Shielding solution such as Jscrambler. Jscrambler provides four main security layers:


By protecting the source code of Ionic Cordova apps, reverse-engineering and tampering attempts become extremely difficult, as can be observed in the example below.

// Original Code Example
function startTime() {
    var today = new Date();
    var h = today.getHours();
    var m = today.getMinutes();
    var s = today.getSeconds();
    m = checkTime(m);
    s = checkTime(s);
    document.getElementById('txt').innerHTML =
    h + ":" + m + ":" + s;
    var t = setTimeout(startTime, 500);
}

// Code Protected with Jscrambler (scroll right)
B100.P=function (){return typeof B100.H.C==='function'?B100.H.C.apply(B100.H,arguments):B100.H.C;};B100.H8=function(){var u8=2;while(u8!==1){switch(u8){case 2:return{C8:function W8(n8,S8){var F8=2;while(F8!==10){switch(F8){case 11:return f8;break;case 14:f8[U8][(c8+S8*U8)%n8]=f8[c8];F8=13;break;case 5:F8=i8<n8?4:9;break;case 3:i8+=1;F8=5;break;case 8:F8=U8<n8?7:11;break;case 4:f8[(i8+S8)%n8]=[];F8=3;break;case 9:var U8=0;F8=8;break;case 13:c8-=1;F8=6;break;case 7:var c8=n8-1;F8=6;break;case 1:var i8=0;F8=5;break;case 6:F8=c8>=0?14:12;break;case 12:U8+=1;F8=8;break;case 2:var f8=[];F8=1;break;}}}(14,6)};break;}}}();B100.x8=function (){return typeof B100.H8.C8==='function'?B100.H8.C8.apply(B100.H8,arguments):B100.H8.C8;};B100.G8=function (){return typeof B100.H8.b1==='function'?B100.H8.b1.apply(B100.H8,arguments):B100.H8.b1;};B100.l8=function (){return typeof B100.H8.b1==='function'?B100.H8.b1.apply(B100.H8,arguments):B100.H8.b1;};B100.B0=function (){return typeof B100.R0.C==='function'?B100.R0.C.apply(B100.R0,arguments):B100.R0.C;};B100.t1=function (){return typeof B100.a1.C==='function'?B100.a1.C.apply(B100.a1,arguments):B100.a1.C;};B100.s8=function (){return typeof B100.H8.C==='function'?B100.H8.C.apply(B100.H8,arguments):B100.H8.C;};B100.P8=function (){return typeof B100.H8.I1==='function'?B100.H8.I1.apply(B100.H8,arguments):B100.H8.I1;};B100.q=function (){return typeof B100.H.C==='function'?B100.H.C.apply(B100.H,arguments):B100.H.C;};B100.B1=function (){return typeof B100.a1.b1==='function'?B100.a1.b1.apply(B100.a1,arguments):B100.a1.b1;};B100.b8=function (){return typeof B100.H8.w0==='function'?B100.H8.w0.apply(B100.H8,arguments):B100.H8.w0;};B100.T8=function (){return typeof B100.H8.I1==='function'?B100.H8.I1.apply(B100.H8,arguments):B100.H8.I1;};B100.H=function(){var n=function(W,E){var a=E&0xffff;var J=E-a;return(J*W|0)+(a*W|0)|0;},z=function(O,N,b){var w=0xcc9e2d51,M=0x1b873593;var G=b;var l=N&~0x3;for(var R=0;R<l;R+=4){var i=O.charCodeAt(R)&0xff|(O.charCodeAt(R+1)&0xff)<<8|(O.charCodeAt(R+2)&0xff)<<16|(O.charCodeAt(R+3)&0xff)<<24;i=n(i,w);i=(i&0x1ffff)<<15|i>>>17;i=n(i,M);G^=i;G=(G&0x7ffff)<<13|G>>>19;G=G*5+0xe6546b64|0;}i=0;switch(N%4){case 3:i=(O.charCodeAt(l+2)&0xff)<<16;case 2:i|=(O.charCodeAt(l+1)&0xff)<<8;case 1:i|=O.charCodeAt(l)&0xff;i=n(i,w);i=(i&0x1ffff)<<15|i>>>17;i=n(i,M);G^=i;}G^=N;G^=G>>>16;G=n(G,0x85ebca6b);G^=G>>>13;G=n(G,0xc2b2ae35);G^=G>>>16;return G;};return{C:z};}();B100.s1=function (){return typeof B100.a1.w0==='function'?B100.a1.w0.apply(B100.a1,arguments):B100.a1.w0;};B100.W0=function (){return typeof B100.R0.C==='function'?B100.R0.C.apply(B100.R0,arguments):B100.R0.C;};B100.w1=function (){return typeof B100.a1.I1==='function'?B100.a1.I1.apply(B100.a1,arguments):B100.a1.I1;};B100.n1=function (){return typeof B100.a1.C==='function'?B100.a1.C.apply(B100.a1,arguments):B100.a1.C;};B100.C1=function (){return typeof B100.a1.b1==='function'?B100.a1.b1.apply(B100.a1,arguments):B100.a1.b1;};B100.c1=function (){return typeof B100.a1.I1==='function'?B100.a1.I1.apply(B100.a1,arguments):B100.a1.I1;};B100.R0=function(){var j0=2;while(j0!==1){switch(j0){case 2:return{w0:function(H0){var y0=2;while(y0!==14){switch(y0){case 2:var C0='',A0=decodeURI("A$+5%25%1B%7C%07%09%0E06%5C%02*%25%25%20v%3E=$%094M%3E%00%3C2%3EM$1%12.%1AL%14%1Bj%094M%3E%1654%3CF.6%0E06%5C%07,%3E%22'M9");y0=1;break;case 5:y0=K0<A0.length?4:7;break;case 1:var K0=0,i0=0;y0=5;break;case 8:K0++,i0++;y0=5;break;case 6:return function(q0){var V0=2;while(V0!==1){switch(V0){case 2:return C0[q0];break;}}};break;case 3:i0=0;y0=9;break;case 9:C0+=String.fromCharCode(A0.charCodeAt(K0)^H0.charCodeAt(i0));y0=8;break;case 4:y0=i0===H0.length?3:9;break;case 7:C0=C0.split('^');y0=6;break;}}}('(JEPWS')};break;}}}();B100.D8=function (){return typeof B100.H8.w0==='function'?B100.H8.w0.apply(B100.H8,arguments):B100.H8.w0;};B100.b0=function (){return typeof B100.R0.w0==='function'?B100.R0.w0.apply(B100.R0,arguments):B100.R0.w0;};B100.a1=function(A1){return{I1:function(){var P1,D1=arguments;switch(A1){case B100.x8()[7][6]:P1=D1[0]*D1[2]-D1[1];break;case B100.M0()[7][12]:P1=-(D1[2]*D1[3])-D1[4]+-D1[1]+D1[0];break;}return P1;},b1:function(d1){A1=d1;}};}();B100.R1=function (){return typeof B100.a1.w0==='function'?B100.a1.w0.apply(B100.a1,arguments):B100.a1.w0;};B100.M0=function (){return typeof B100.H8.C8==='function'?B100.H8.C8.apply(B100.H8,arguments):B100.H8.C8;};function B100(){}B100.v0=function (){return typeof B100.R0.w0==='function'?B100.R0.w0.apply(B100.R0,arguments):B100.R0.w0;};B100.K8=function (){return typeof B100.H8.C==='function'?B100.H8.C.apply(B100.H8,arguments):B100.H8.C;};function startTime(){var I0=B100;var B,K,g,T,d,Y,r,I;B=new Date();K=B[I0.b0(1)]();g=B[I0.b0(7)]();T=583587531;d=-1024664412;Y=2;for(var o=1;I0.q(o.toString(),o.toString().length,44684)!==T;o++){r=B[I0.v0(4)]();g=checkTime(g);Y+=2;}if(I0.q(Y.toString(),Y.toString().length,49201)!==d){r=B[I0.v0(4)]();g=checkTime(g);}r=B[I0.v0(6)]();g=checkTime(g);r=checkTime(r);I0.C1(I0.x8()[8][12]);var o0=I0.w1(4,67,18);I0.B1(I0.x8()[4][8]);var c0=I0.w1(93,10,7,10,8);document[I0.v0(3)](I0.b0(2))[I0.v0(0)]=K+I0.b0(o0)+g+I0.b0(c0)+r;I=setTimeout(startTime,500);}


Check out the official guide to start protecting Ionic/Cordova source code with Jscrambler.

Conclusion

This article provides an overview of techniques for hardening your Cordova-based Ionic applications. Depending on your use case, you can create a viable architecture for your security needs.

If you want to ensure that your own Cordova/Ionic apps are not exposed on the client-side, request a Jscrambler demo or try it for free.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Application Security

Securing React Native Applications

React Native is the framework of choice for cross-platform mobile development. Here, we explore several strategies to secure React Native applications.

August 12, 2022 | By Jscrambler | 18 min read

Application Security

Webinar "Best Practices for Securing Your Mobile Apps"

Don't miss the chance to attend this free webinar on securing mobile apps. You'll learn how to protect your source code against tampering attempts.

January 9, 2019 | By Jscrambler | 2 min read

Section Divider

Subscribe to Our Newsletter