Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Protecting Hybrid Mobile Apps With Ionic and Jscrambler

DZone's Guide to

Protecting Hybrid Mobile Apps With Ionic and Jscrambler

You can secure your Ionic applications using Jscrambler by integrating it in the build process. It works for either Ionic v1 or v2, giving devs a bit of breathing room.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

Ionic is an open source framework designed to build native-like mobile web applications which target the major mobile operating systems. Targeting different systems with the same codebase speeds up the development process while reducing the time to market and maintainability efforts.

Ionic is built upon two major frameworks, Apache's Cordova and Google's Angular. It has a great ecosystem behind it, with comprehensive documentation and a marketplace where you can find many themes and plugins to get you started.

This guide will explain how to secure your Ionic application using Jscrambler, integrating it in the build process, which works for Ionic's v1 (with Angular 1) and v2 (with Angular 2). We will focus on Ionic v2 first. If you want to protect your Ionic v1 app please refer to the Working With Ionic 1 Annex at the end of the post.

How to Create an Ionic Application

Getting started with Ionic is pretty easy. Firstly, make sure you install Ionic alongside Cordova

npm install -g cordova ionic


To get up and running, install one of the starter projects, either blanktabs, or sidemenu. For this guide, we will be sticking with the tabs template.

ionic start myProtectedApp tabs --v2


Ionic will download and install all the dependencies of the template project, based on Angular 2, which uses TypeScript.

That's all we need to have a functional Ionic app. Check if everything is in place by running the app in the browser. By default, it will run on localhost on port 8100.

ionic serve


If you need more information on getting started, please refer to the official documentation.

The Structure of an Ionic App

The base project structure of an Ionic application is very similar to Cordova's directory structure

myProtectedApp/  
|-- config.xml
|-- hooks/
|-- www/
|-- platforms/
| |-- android/
| |-- windows/
| |-- ios/
|-- plugins/
|-- resources/
|-- src/
  • config.xml contains the configuration of your Ionic application.
  • Cordova hooks are placed inside the hooks directory. Here lie the scripts which are executed before or after specific tasks such as building and emulating. These are useful since they allow the customization of the app's build process as we will shortly discuss.
  • The www directory contains all the source code and assets of the application such as HTML, CSS and JavaScript.
  • The platforms directory contains the code and scripts necessary to deploy the Ionic application to the desired platform.
  • Any plugins required by the application will be extracted to the pluginsdirectory.
  • The resources directory contains images to be used as logo and splash of the application across different resolutions.
  • The src directory features all the source code of the application. The sources are then built and packed into the www directory (which Cordova uses to deploy to each platform).

The structure of the src directory depends on the build tool being used. Some boilerplates use webpack, a module bundler which allows for a great level of customization when building your app. The official Ionic templates, however, discontinued their gulp build process in favor of a custom one, ionic-app-scripts.

If you're using the new build process with ionic-app-scripts then your src directory must follow a specific structure

|-- src/
| |-- app/
| |-- assets/
| |-- pages/
| | |-- page1/
| | |-- page2/
| |-- theme/
  • The app subdirectory contains the modules and components of your application, including the setups for dev and prod environments.
  • The assets subdirectory is similar to the resources directory, though the files in this folder are transversal to the device size.
  • The pages directory comprises folders for each page of the application. Each folder contains an htmlscss and typescript file responsible for giving the page form and behaviour.
  • The theme folder contains scss files which allow for the customization of the application's theme.

Integrating Jscrambler in the Build Process

Installing Dependencies

The first step of our integration with Jscrambler is the installation of the Jscrambler API Client .

npm install jscrambler --save-dev


You should also install the desired build platform. Ionic supports androidiosand windows. You can find more information about platforms here).

ionic platform add <platform>


Hooking Up Jscrambler

In order to integrate Jscrambler in our application's build process, we need to take advantage of Cordova's hooks. These hooks are tasks to be executed before or after certain commands (such as buildor emulate).

The protection process should take place after the prepare step, where all the assets from the www directory and plugins are placed in the respective platform folder. In android's case, for example, the files in the www directory will be copied to platforms/android/assets/www.

ionic prepare


The prepare step is performed every time the following commands are issued

  • ionic prepare
  • ionic platform add
  • ionic build
  • ionic run

In the hooks folder, there should be an after_prepare directory (since the template creates it). If not, create it.

Now it's time to write our new hook, which will protect our application. Please be aware that, if you have other hooks and/or you want to execute them in order, you need to prefix the file name with a number. For example, if we have two files, named 010_hook_1.js and 020_hook_2.jshook_1 will always be performed before hook_2.

Create the jscrambler.js file in the after_prepare directory

#!/usr/bin/env node

var jscrambler = require('jscrambler').default;

jscrambler.protectAndDownload({  
  'keys': {
    'accessKey': 'JSCRAMBLER_ACCESS_KEY',
    'secretKey': 'JSCRAMBLER_SECRET_KEY'
  },
  'applicationId': 'YOUR_APP_ID',
  'filesSrc': [
    './platform/android/assets/www/*.{js,html}',
    './platform/android/assets/www/js/*.js',
    './platform/ios/www/*.{js,html}',
    './platform/ios/www/js/*.js'
  ],
  'filesDest': './',
  'params': [
    {
      'name': 'whitespaceRemoval'
    },
    {
      'name': 'duplicateLiteralsRemoval'
    },
    {
      'name': 'functionReordering'
    },
    {
      'name': 'dotToBracketNotation'
    },
    {
      'name': 'functionOutlining'
    },
    {
      'name': 'booleanToAnything'
    },
    {
      'name': 'stringSplitting',
      'options': {
        'chunk': 0.25
      }
    },
    {
      'name': 'identifiersRenaming',
    },
    {
      'name': 'propertyKeysReordering'
    },
    {
      'name': 'propertyKeysObfuscation'
    }
  ],
  'areSubscribersOrdered': false,
  'applicationTypes': {
    'webBrowserApp': false,
    'desktopApp': false,
    'serverApp': false,
    'hybridMobileApp': false,
    'javascriptNativeApp': false,
    'html5GameApp': false
  },
  'languageSpecifications': {
    'es5': true,
    'es6': false,
    'es7': false
  }
})
.then(function() {
  console.log('Jscrambler done!');
});


Feel free to change params, making use of any transformation Jscrambler has to offer. You can find all the transformations available here.

You should also change the filesSrc depending on the platform you're targeting and on how your www directory is structured.

By using filesDest: './', the files in our platform folder will be replaced by their protected version. This is fine since the prepare step copied our files from the www folder to the respective platform directory, leaving our original files intact. Had we used the www folder as our source then our original files would be replaced, which isn’t desirable.

You can inspect the destination folder to verify if the protection was applied as requested.

Building the Application

Note: if you're using Ionic v1 please check out Working with Ionic v1 Annex 
(found at the end) and then return here. The Working with Ionic v1 section addresses some issues encountered while protecting Angular 1 regarding the way dependencies are injected.

We are now ready to protect our code and build our application. Make sure the hook we just created has execute permissions:

chmod +x hooks/after_prepare/jscrambler.js


Now we can run any of the commands which require the prepare step, such as...

ionic prepare


...or, if we want to generate the application file

ionic build --release <platform>


The protected files should be in the destination directory specified.

The build for android places the apk file generated on platforms/android/build/outputs/apk. The apk will not run on a device unless it is signed first. If you try to install an unsigned apk then the device will alert for a parsing error.

In order to run it on an android device, we need to generate a key. If you have JDK installed start by generating a key

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000


Then sign the apk with it

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore android-release-unsigned.apk alias_name


Finally, optimize the application file using zipalign. You can find the zipaligntool under path/to/Android/sdk/build-tools/VERSION/zipalign.

zipalign -v 4 android-release-unsigned.apk myProtectedApp.apk


And you're done! Now you have the app file ready to use.

You can verify if your apk file has the protected assets by using any file extracting application. The files should be placed under assets/www.

If you need further information on how to publish your app, or on how to deploy to iOS, which requires you to register as an Apple Developer, please follow the Ionic publishing guide.

Conclusion

Ionic is an effective framework for creating powerful, responsive and multi-platform applications without the need of native platform knowledge, speeding up the time of development.

By combining the build process with Jscrambler, you can have your code protected on a mobile platform by simply adding a hook that executes before building your app, therefore saving you time in the build or deployment process.

Annex: Working With Ionic v1

Working with Ionic v1 and Jscrambler is very similar to v2. There are only some minor adjustments that need to be made in order to have the Angular 1 code working. You can follow the How to Create an Ionic Application section as if it were for v1 (just remove the --v2 flag when creating the app).

When you finish the next sections you can pick back up on the Integrating Jscrambler in the Build Process section.

Structure of the Application

Ionic v1 apps do not feature a src or resources directory out of the box. Instead, they work directly with the www directory, where all the source code is. On this version, the Ionic default look can be customized by modifying the files on the scssdirectory, instead of src/themes.

The structure of the www directory is up to you, since there are many ways of structuring an Angular application. The app can be structured by purpose, having a distinct separation between directives, controllers, and templates, or by feature, where each directory contains all the files necessary to reproduce a feature.

For more information on Angular best practices please consult this style guide.

Managing Dependencies

Since Angular 1 manages dependencies by passing specific variables such as $scope and $stateProvider, the identifiers renaming transformation has to be performed with an option excluding all the names prefixed with $

'params': [  
  {
    "name": "identifiersRenaming",
    "options": {
      "mode": "SAFEST",
      "excludeList": [
        "$*"
      ]
    }
  }
]


In order to perform a renaming without the need of an exclude list, we must refactor our Angular code. We need to correctly inject the dependencies to the modules which require them, as depicted in Angular's documentation about minification.

The www/js/controllers.js file of the default tabs application contains the following code

// www/js/controllers.js

angular.module('starter.controllers', [])

.controller('DashCtrl', function($scope) {})

.controller('ChatsCtrl', function($scope, Chats) {
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
})

.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
  $scope.chat = Chats.get($stateParams.chatId);
})

.controller('AccountCtrl', function($scope) {
  $scope.settings = {
    enableFriends: true
  };
});


In order to drop the exclude list, we must refactor the controllers. Create a function for each one, inject the dependencies and then assign the controller to the module.

// www/js/controllers.js

angular.module('starter.controllers', []);

// Create a function for the controller
function ChatsCtrl($scope, Chats){  
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
}

// Create a $inject property with the dependencies
ChatsCtrl.$inject = ['$scope', 'Chats'];

// Assign the controller to the module
angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);


You can find the refactored Javascript source files in this GitHub repository.

Now we can drop the options of the identifiers renaming transformation. Note that the mode will default to SAFEST.

'params': [  
  {
    "name": "identifiersRenaming"
  }
]


Check if everything is OK by running...

ionic run <platform>


...and checking if the app works properly.

Function Reordering and RASP

Jscrambler's Runtime Application Self-Protection transformation (also known as self-defending) converts function declarations into variable declarations. The value of the newly created variable is a function expression

'params': [  
  {
    "name": "functionReordering"
  },
  {
    "name": "selfDefending"
  }
]
// Function declaration
function foo() {  
    return 'foo!';
}

// Variable with a function expression
var bar = function() {  
    return 'bar!';
}


Applying the self-defending transformation with function reordering may break the code since Angular is unable to resolve the dependencies resulting from the refactoring made in the previous section.

// Previously refactored code before applying any transformation

angular.module('starter.controllers', []);

function ChatsCtrl($scope, Chats){  
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
}

ChatsCtrl.$inject = ['$scope', 'Chats'];

angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);


If we apply only the self-defending and function reordering transformations, we obtain the following code

// Protected code resulting of applying selfDefending with functionReordering
// Will break the app

// Even though the ChatsCtrl variable is declared (hoisted)
// it's not assigned to an object
// Results in the error: Unable to set property $inject of undefined

ChatsCtrl.$inject = ['$scope', 'Chats']; // Error

// Var declaration resulting of the selfDefending transformation
// New variable is created with the previously declared function
var ChatsCtrl = function () {  
  /* protected code */
}

angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);


This situation is caused by the reorder of the functions. Sometimes, dependencies are injected before the variable is initialized, which breaks the application

To avoid this issue, we need to do another refactor, this time converting our function declarations into variable declarations with function expressions.

// www/js/controllers.js

// Newly refactored code
// Will not break the app

// Since the function is assigned to a variable it will not be reordered
// therefore dependencies will be added after the variable is declared

// Instead of `function ChatsCtrl($scope, Chats){...}`
var ChatsCtrl = function ($scope, Chats){  
  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
}

ChatsCtrl.$inject = ['$scope', 'Chats'];

angular.module('starter.controllers').controller('ChatsCtrl', ChatsCtrl);


These refactors should suffice in order to have Jscrambler protecting your code.

Now you're ready to integrate Jscrambler in the build process, please go back to the Integrating Jscrambler in the Build Process section.

As previously stated, you can find the refactored JavaScript source files in this GitHub repository.

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
ionic ,application security ,mobile ,jscrambler ,tutorial

Published at DZone with permission of Carlos Matias. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}