Ionic (and other hybrid apps) code protection

Jose I Santa Cruz G
Frontend Weekly
Published in
5 min readJan 30, 2018

--

Code talkers — vía https://xkcd.com/257/

Code protection is a common (supposed to be) drawback when speaking about hybrid apps. At least on Android, decompiling an app and accessing the code is an easy task, so security again becomes an argument, even more when it comes to deciding on developing an hybrid or native app.

I’ve heard more than a couple of times that Ionic apps are insecure. But, is it true? IMHO and based on my own experience developing an enterprise eBanking solution on Ionic, there’s no code 100% secure. The least expected to be done is to take OWASP’s top 10 mobile risks and try to cover them. But even then, and unless your app retrieves an encrypted code, and decrypts it on the app (in a supposed-to-be secure way), there’s always something that makes your code “insecure”.
This applies the same to hybrid and native apps, if someone can decompile your app, it will be done, and there will be “insecure” code.

The sad part is that you may want to make your app the most secure app in the App store, but every developer involved MUST be aligned in making secure and quality code.

Nevertheless, in the hybrid universe, you can mitigate these kind of attacks by obfuscating your code. For those who aren’t familiar with obfuscation, this is uglifying the code to a level is not directly understandable by humans (anyway, if you got here because of the article’s title, I’m almost sure you know what I meant).

When compiling with — prod — release parameters, there are some optimizations already done by ionic-cli:

  • The code is minified
  • The code is uglified (sort of)
  • The code is arranged in one or many javaScript files (depending if your’re using lazy loading or not)

This leads to several performance improvements, but code isn’t secure, just a little harder to read. So let’s make some changes that can improve our code’s security perception.

  1. All network traffic has to go over a secure channel (that means HTTPS). Use valid certificates in your server and not self signed ones. Let’s Encrypt is a good starting point.
  2. Check the server fingerprint, to ensure your app is “talking” with the server it is supposed to be talking with (and not an evil server impersonating the good one).
  3. If your app is consuming a REST API (hopefully stateless, as it should be) and requires session validation, use JWT with a short expiration period, and use the refresh token logic (make your research).
  4. Application keys have to be encoded somehow, and not be placed as plain text variables.
  5. All sensitive app-server data interchange must require a user/token/device validation.
  6. Ensure (using some anti-tampering strategy) that the code in execution is the code effectively distributed with your app.
  7. Use a secure storage to store any information. If the app has to store user’s data, this information must be visible only by the app, and not by any third party not authorized apps.

But what about the code?

The package.json file of a Ionic app has a config section, where you can specify some specific settings for the compilation task executed by ionic-app-script.

Copy the original uglify.config.js file and adjust the settings according to the Uglify 2’s options.

// https://www.npmjs.com/package/uglify-es 
module.exports = {
parse: {
html5_comments: false
},
/**
* mangle: uglify 2's mangle option
*/
mangle: true,
/**
* compress: uglify 2's compress option
*/
compress: {
toplevel: true,
pure_getters: true,
drop_console: true,
drop_debugger: true,
evaluate: true
},
output: {
beautify: false
}
};

Above I placed the settings I’m using to get a nicely clean, and mangled code. Place this file in a ./config folder in your project’s root.

Edit your package.json’s config section to:

"config": {
"ionic_uglifyjs": "./config/uglifyjs.config.js"
},

Copy pasters BEWARE: The above code is only the config section of your app’s package.json file, not the whole file. DO NOT REPLACE YOUR FILE, add the section or edit the existing one.

After building there’re no debugger or console instructions. But the problem here is that even though the code is mangled and minimized, it is still readable, and any URLs, keys or whatever you need to connect your app to a server is still visible and readable.

When working with Ionic-v1 I found a nice Gulp plugin called Gnirts. It turns any text/string placed between the @mangle comments into a function call, that when executed returns the string value.
I tried unsuccesfully to use Gnirts with Ionic (v2 ≤) so I had to look for another obfuscation method. This search lead to the Javascript Obfuscator script.

Using the Javascript Obfuscator requires ejecting the Webpack configuration for the Ionic project and adding the Webpack obfuscator to the plugin array.

  • Eject the webpack configuration: Copy the original webpack.config.js file to your config folder (same as you probably did with the original uglify.config.js file).
  • Adjust package.json file config section: Add the new line in boldface:
"config": {
"ionic_uglifyjs": "./config/uglifyjs.config.js",
"ionic_webpack": "./config/webpack.config.js"
},
  • Install Webpack obfuscator: If you haven’t done it yet, follow the instructions.
  • Edit the webpack.config.js file: This is just the plugins section:
plugins: [
new JavaScriptObfuscator ({
compact: true,
controlFlowFlattening: false,
deadCodeInjection: false,
debugProtection: true,
debugProtectionInterval: false,
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
renameGlobals: true,
rotateStringArray: true,
selfDefending: true,
stringArray: true,
stringArrayEncoding: true,
stringArrayThreshold: 0.75,
unicodeEscapeSequence: false,
transformObjectKeys: true
}, []),
ionicWebpackFactory.getIonicEnvironmentPlugin(),
ionicWebpackFactory.getCommonChunksPlugin(),
// This array is incomplete

OK, I placed as comment on the code because the array may be incomplete dependeing on which section you’re copy-pasting it. I suggest this extreme obfuscation settings are used only on production builds.

After running some ionic build tries, you should check what’s the result.

The code on the ./www/js folder (where every build stores the compilation process results) if extremely obfuscates. Function and variable names are mangled in a hexadecimal code, most strings are Base64 encoded, console instructions are nulled, and when someone opens the browser’s developers options the code is stopped on an eternal debug cycle (self protecting code).

Your code is now almost secure, but there’s a price to pay. Performance may have been compromised in some level. Depending on the obfuscator settings performance can vary between 30% to a 100% slower than no obfuscations, so you need to do a lot of testing. And don’ t forget that performance is also linked to each device capability, so an app that works well on an iPhone, probably doesn’ t have the same performance in an Android device. Browser tests are just a reference of what to expect.

Hybrid apps aren’t less secure than native ones, not hybrid apps fault, just faulty developers not coding apps the right way (if there’s one).

--

--

Jose I Santa Cruz G
Frontend Weekly

Polyglot senior software engineer, amateur guitar & bass player, geek, husband and dog father. Not precisely in that order.