Author Archives: Nathanael Anderson

Another pointless NativeScript Security Plugin....

So, I saw this blog (https://blog.nativescript.org/nativescript-ios-security/index.html) article today, and was thinking awesome someone is actually talking about security in NativeScript apps... My elation was very short lived. Shortly, after starting to read it, I'm like, oh my G*D! Bad security advice is way worse than no security advice! At least the other insecure plugin NativeScript/Secure-Storage has a point, this one....

Well, if you read any of my prior posts on the in-security of React Native, NativeScript, Ionic here, and here. You might have guessed what my response to this is...

Lets show how trivial this is to bypass...

Creating the Demo Applications

First, lets create a throw away folder, and two simple test apps.

mkdir bypass
chdir bypass
ns create demoapp --js
ns create shellbypass --js
cd shellbypass
ns plugin add @nativescript/ios-security
cd ../demoapp
ns plugin add @nativescript/ios-security

Creating a Demo to test it...

The demoapp is our awesome multi-year released app that we have poured hundreds of hours and millions of dollars in. The shellbypass is the hackers app.

Open your favorite editor... Mine is phpStorm and it should look like this:

Next you need to open the demoapp/app/main-page.js file.

Then at the top we want to add this awesome plugin so add:

const IOSSecurity = require("@nativescript/ios-security").IOSSecurity;

Then in right below the page.bindingContext = ... we need to add our security check...

let amInEmulator = false;
if (!global.android) {
  amInEmulator = IOSSecurity.amIRunInEmulator();
}

if (amInEmulator) {
    console.log("Hey I'm in a emulator");
    page.bindingContext.set('message', "I'm in a Emulator");
  } else {
    console.log("Hey, we are on a real device!");
    page.bindingContext.set('message', "I'm on a real device!");
}

So the main-page.js should look like this (with all comments deleted):

Ok, lets run this on a simulator.

> ns run ios

And look our security check worked perfectly!

You can see on the top it is built for a simulator; and the console.log says: "Hey I'm in a emulator". So we have deployed everything correctly. Now switching roles to the attacker, this app won't run on an emulator, or will it?

Lets just pretend we uploaded it the app store and then used one of the techniques I outlines in the other blog posts to download your application IPA directly from Apple to my mac (or you can just use a jail broken phone). The IPA will look something like this:

Lets go into the "app" sub-folder, as it contains our original NativeScript app code.

So what do we have here... I outlined them in the other blog posts but we will dip briefly into it.. First lets copy the bundle.js, runtime.js and vendor.js to our "shellbypass" app into a new folder called "other". Now normally I would run some my custom tooling to re-extract the code back out of bundle.js/vendor.js into all their separate files, so I can easily rebuild the app from virtually the same code that you had. But in this case I'm lazy and I want you to be able to easily follow along so we are just going to use the raw files from the "original" app..

If we are on an actual jail broken phone, we actually don't have to do the "Creating a Shell" step, you can skip all the way down to "Removing Security" as you can edit the files directly on the device...

Creating a Shell

  1. Delete everything in app/ folder except the "app.js" file.

2. make app.js just have the following line of code to make sure that the IOSSecurity native code is built into the new NS app:

const IOSSecurity = require("@nativescript/ios-security").IOSSecurity;

3. Edit the webpack.config.js and make it look like this:

const webpack = require("@nativescript/webpack");
const fs = require('fs');
const platformInfo = require("@nativescript/webpack/dist/helpers/platform");

module.exports = (env) => {
webpack.init(env);

let output = platformInfo.getAbsoluteDistPath() + "/";
if (fs.existsSync(output)) {
fs.rmdirSync(output, {recursive: true});
}
setWait(output);

return webpack.resolveConfig();
};

function setWait(output) {
if (!fs.existsSync(output+"vendor.js") || !fs.existsSync(output+"runtime.js")) {
setTimeout(() => { setWait(output); }, 100)
return;
}
fs.copyFileSync("./other/bundle.js", output+"bundle.js");
fs.copyFileSync("./other/runtime.js", output+"runtime.js");
fs.copyFileSync("./other/vendor.js", output+"vendor.js");
console.log("Copied");
}

Again I'm being very lazy, basically we are letting webpack work normally, then copying the original demoapp files into our hackers shellbypass app.

In all reality splitting the code back out of the bundle into its separate files makes it a lot easier, but my tooling is not very clean so I'm not ready to release it yet. But it is trivial if you read my prior blogs to split the files back out and de-minimize them...

So now we have a shell app that should run the original demoapp code as is. Lets test it.

> ns run ios --no-hmr

As you can see this app is called "shellbypass" and it is printing the same "Hey I'm in a emulator".

WooHoo, we have successfully cloned your million dollar app into our own free NativeScript shell.

Removing Security

All of that just to show you have this plugin doesn't matter... So first there is two ways to do this... I'll show you both ways, just to show you how trivial it is to remove these types of check's.

Bundle.js changes:

Search for "amIRunInEmulator" -- Look we found it on line 205... Lets just comment that right out...

Now what happens when I run my app...

Now the app prints "Hey, we are on a real device!" The awesome plugin's security sure did stop me from running on an emulator, didn't it? The app you so carefully programmed, and may have even spent years on -- I just simply removed the check and ran it and now I'm playing with it on a simulator or a jail broken device.

However, the vastly better method is to just change the vendor.js file, so that I don't have to find every place you put the checks in your code. (So first undo the comment we just added in the bundle.js, so it is back to "stock" demoapp)

Vendor.js changes:

This is the vastly superior solution, lets look about line 46849, and just return false....

All I would do is return false on basically every single one of these functions in the IOSSecurity class, and the plugin doesn't do a single thing no matter how many times, nor where you called it inside your application (the bundle.js).... Defeated in less that 5 minutes...

Wrapping it all up...

The problem with security measures like this, is that NativeScript, React Native, Cordova/PhoneGap, Ionic is they all ship the source code ships inside the app. It is trivial to extract it out, create a new shell and make the changes to make it my own "clone" project or to steal/corrupt your data by using your endpoints. If you plan on using something like this plugin you need to use a product like JSScrambler or https://AppProtection.net (my own product which encrypts the NativeScript source code). If you just ship the app using minimization (pointless for security, but useful for optimization). Please read the prior blog posts on how easy it is to pull everything out.

If you are interested in more info on my current NativeScript solution AppProtection, or if you want to help fund the vastly superior solution that eventually can work for both React Native and NativeScript with no coding changes in your app; please contact me at nathan@master.technology.

Quickly adding .desktop menu files

For those who use Linux with a Gui, you may be like me and haven't found a good way to create .desktop files for any new applications you downloaded. I googled and tried many stackoverflow posts, and found Alacarte which allows you to edit menu items and re-org your menu. But never found anything that I could use from the CLI.

This weekend I spent a couple hours and build a simple tool called "desktopmenuitem".

It allows me to do this:

Animated adding of a new .desktop file

Basically you can just run it and point it at the executable and it does the rest. However, it does allow you to change up a lot of items from the cli.

Hopefully this helps others. You can install it via npm;

npm i -g @master.technology/desktopmenuitem

and the repo for the source code and issues is here: https://github.com/master-technology/desktop-menuitem

PSA: Your Android and iOS Application is not secure...

I was talking to a community member about some of the sites I run (and I mentioned a site called https://AppProtection.net) as they didn't realize I also do a lot of server side work. However, she was shocked that the source code for her app was easily accessible by everyone.

If you write an application with NativeScript, React Native, Cordova/PhoneGap/Ionic (and many other platforms) are you aware you ship the entire JavaScript source code, styles, layout code in your app.

For simplicity we are going to look at a NativeScript application that we will generate quickly so you can easily follow along. These steps apply equally for React Native and the Cordova based eco-systems.

You can uses tools like apkeep to download APK's directly from Google play store, or you can use something like Apple Configurator 2 on Macintosh to download the IPA from the Apple iOS store to your computer. Anything published to either store can be retrieved to your computer directly, and that is when the fun begins...

How easy is it?

 ns create demo --js
 cd demo

This command creates a demo application, for simplicity I'm just going to use pure JS, but again this applies to all the NativeScript flavors, Vue, Angular, TypeScript, React, etc.

Now we are going to make some minor changes to the main-page.js file to look like this, as we want emphasize that any secrets are NOT secret! This includes all Tokens, your http(s) URL endpoints, API keys, passwords, usernames, etc....

import { createViewModel } from './main-view-model';

const secret_HTTP_APIKey = "Hi_IM_A_SECRET_TOKEN_USED_ACCESS_SOME_API";
const private_Firebase_Token = "Hi_IM_your_Secret_Firebase_token";

export function onNavigatingTo(args) {
  const page = args.object
  doSomethingWith(secret_HTTP_APIKey);
  doSomethingWith(private_Firebase_Token);

  page.bindingContext = createViewModel()
}

function doSomethingWith(value) {
  <strong><em>console</em></strong>.log("Pretend HTTP Call with token", value);
}

Now we will build it:

ns build android --copy-to .

I'm using the <strong>--copy-to .</strong> so that we copy the final apk to this folder so we don't have to go looking for it in the platforms/android... folder, you should have something like this now...

The app-debug.apk (or .ipa for ios) is basically what is uploaded to Google Play or the Apple iOS store. Lets look at this file. If you unaware a .apk is just a .zip file with a different file extension. In my case, I just renamed it to .zip so that Midnight Commander (my favorite terminal shell) can navigate inside and view files directly....

Opening up the .zip/.apk file, and for NativeScript navigating to assets/app you will see the files that matter. The bundle.js is your application code, the vendor.js is all the plugins and NativeScript core code, so 99.9% of the time this is a pointless file for reversing your app. However, if you have custom plugins this would have that code. You can also see any worker javascript files if you are using any workers, and angular has some code splitting that can create some other separate javascript files. The bundle is typically the most important file... Lets just copy it out of the zip file so we can open it into a real editor....

How secure is my JavaScript, lets see the main-page.js?

Opening bundle.js up in PHPStorm, this is what we see:

Opening up bundle.js

Interesting it tells us we have 6 files that were compiled into this app.

Lets look for the main-page.js inside the bundle! Simply do a search for main-page.js and this what we find around line 100 in the bundle.js file (that is because the prior 80 lines or so is all the CSS that was bundled from app.css and the default imported theme css files)

main-page.js entry

Wow, 100% full source code including all your awesome secret keys and tokens (which I selected)... If you compare this to our file above, the only difference in this code is that we original did <meta http-equiv="content-type" content="text/html; charset=utf-8">export function onNavigatingTo(args) and this removed the <strong>export</strong> from the function on 114 and added it on line 105 - 107 as a __webpack_exports__. Otherwise this code is identical.

What about the layout, Main-Page.xml?

Lets search for main-page.xml, oh it shows up on line 193...

main-page.xml entry

It is just one long string... What happens if we copy & paste it to a newly created test.xml file. How different is this from the original main-page.xml file, not a single change. It is the exact same file.

Uh oh...

Can anyone tell me how quickly I can take your fully released app, that you spent months or years on creating and make my own 100% full clone? Or perhaps even do things more nefarious and add, edit, or steal any data from any of your private endpoints?

What can you do about this?

There is three things that I'm aware of

  • Minification (Built in to NativeScript & React Native)
  • Obsfucation (Lots of different tools)
  • Encryption

Minification

Well, the first thing people think of the minimization using Terser (or uglify). NativeScript uses Terser by default.

Lets see what happens when we minimize the code using NativeScript 8. Here is the bundle.js file using minification.

Minimized output

This looks hard to parse... But lets see what happens if we use the awesome "Reformat code" option in phpStorm/webStorm...

Code automatically reformatted.

Well, it is different, but still totally readable. How about the main-page.js and our secret keys?

Well that id disappointing, the code is perfectly readable and usable. All your secrets api keys are well, not at all secret. The only thing missing is all the comments were stripped and my "console.log" was removed from the doSomethingWith routine. But the code is very readable and can be dropped virtually as-is in a new main-file.js javascript file. If I wanted to make this file to work in a new app, I would just need to replace "r" with "require" and add the "export" to the onNavigatingTo function and it would be 100% working. Add a couple refactors like "t" to "page" and extract the API keys to variables and it would be virtually identically to the original you wrote. Lets see, hacker 5 minutes, you 5 months.

As you can see minification does nothing really to hinder anyone. But it is a good step for optimization but adds zero security.

Obfuscation

What about obfuscation, this really depends on the tool. I have a blog post about this security of this. However, rather than re-iterate it, it is almost as trivial to reverse as we just did with minification. The only tool that took me longer than a few minutes to reverse was JSScrambler. However for JSScrambler to work you have to set this at its highest level and enable code protections so that a hacker can't easily modify the code. Unfortunately, the last time I tried this is slowed all of the code down by 30-50% the entire life time of the application.

Compiled Code

This is probably the next best thing, if you create a function that has your api keys encrypted in it and you have to call a couple functions to return them before you use them they won't be visible in the source which seems like an awesome win. However, if your JavaScript code has:

const api = getAPI(); 
const key = getKey; 
https.get(api+key);

Do you have any idea how quickly I'll get your api & key, I'll just change the code to be:

const api = getAPI();  
const key = getKey;  
console.log(api+key);

Did you slow me down, yes, by how much -- maybe 15 seconds to modify the apk and upload it to by emulator (or a jail broken android/ios device) . So if you use this method, please do NOT telegraph how to get your Key & api endpoints. At least be more discrete as when I do a search for "https" or "firebase" in your code base you don't want to point me to where I need to go next.

React Native offers a engine called Hermes where it partially compiles the code, this is more secure than the pure JS code mode, but obviously not as secure as pure compiled. However, even in pure compiled code, if you do nothing to obfuscate your keys and api end points, just running strings against it will give you everything a hacker needs to attack your end points.

Encrypted Code

First a major disclaimer here -- I am the author of the AppProtection system, of course I recommend it. Second disclaimer, this is currently NativeScript only, but if I get enough interest for a React Native version, I'll port it as I have all the code all done for both v8 and JavaScriptCore engines. But I have to spend the time adding it to the React Native tooling. Basically this encrypts the source code for your release apps. This is what a release file looks like:

Encrypted Code

The source code AES encrypted, and the only unencrypted copy is in memory (never saved to disk) while the app is running. The actual v8/JSC runtime engine itself has the modifications for understanding AES encrypted JS code, no extra code is shipped in your app. Is it perfect? Nope, but it adds another very difficult wall to climb...

The 100% Solution

Click bait! Sorry there is no 100% solutions. I haven't heard of any of the AppProtection applications being cracked, but that doesn't mean it can't be (or hasn't been). I will tell you upfront everything can be cracked, including AppProtection. Even Denovo which is used in high profile games and has millions of dollars in revenue. Eventually, each new release gets cracked. Everything can be hacked with enough time, patience and knowledge. However, the whole idea of security is you put enough walls in front of your attacker to make him want to go to a much easier target. If they leave, then you have thwarted an attack. Do you really think most attackers or application cloners want to spend weeks pulling your data or cloning your app, or switch to something that only takes them a few minutes?

More information

  • I have another blog post I wrote in 2019, everything is still valid and I discuss other things you can do in your app for security and other security gotcha's.
  • I am available to do consulting work to help you beef up your security both server and mobile. I also do core reviews, and training if your developers need to learn some better practices.
  • You can email me at nathan@master.technology

Fond memories, and onto new adventures....

For those who may or may not know me, I was one of the very earliest NativeScript developers. Started the Facebook and Google groups NativeScript forums. Created some of the very first plugins, helped train a wide number of early NativeScript developers, wrote books, and have some of the most widely used plugins in the entire eco-system. I also run a whole slew of different NativeScript related sites like PNR & SearchCode all under the Master Technology brand. Sufficed to say, I have been a advocate and cornerstone of NativeScript eco-system since it first started.

I also co-founded the nStudio company several years back with a couple other co-owners to focus solely on NativeScript work, and leave Master Technology to do everything else I do (i.e. servers, security, desktop, etc) . nStudio became the largest NativeScript development houses. Then awesome fortune came our way and we were able to take ownership of NativeScript. Finally over the last several months, nStudio was able to join and transfer NativeScript to the OpenJS Foundation (the foundation that supports projects like NodeJs, webpack, electron). So it now has a awesome open source home and foundation to support it for the foreseeable future.

However, unfortunately, during this season of time we had some large disagreements in nStudio leadership, on a myriad of issues. Do to some of these disagreements, I was abruptly terminated from the NativeScript TSC (a couple months back) and finally I officially left nStudio this week. So my work as an NativeScript Core/TSC member ended. I might still push a PR here and there for any client issues I run into, but that is probably the extent of it.

NativeScript...

Before the fallout, I had several ideas for NativeScript that I really wanted to pursue, and never could get any internal nStudio support. Since I am now 100% funded again by my clients work. I'm curious to know if anyone in the eco-system is interested in helping fund any of the following, if we have enough interest I'm willing to pursue any/all of them. I have the experience to make them work.

  • More NativeScript targets:
    • Web (A web front end, so you can use your NS skills and create web sites or apps that run inside of electron/tauri)
    • Desktop (actual native desktop front end, not using html)
    • Sailfish
    • PureOS
  • NativeScript build servers (i.e. easy ability to build everything from the cloud)
  • NativeScript front end, basically like the old SideKick, with lots of helpful tooling.
  • Compiled NativeScript (ios & android), your JS compiled to 100% native code.

Of course you can still hire me for any other NativeScript work you might have, if you have no interest in any of the above ideas.

New Adventures

Interestingly enough towards the end of this period of time, shortly after I finally decided to leave nStudio. Another technology stack was opened up basically exclusively for me to use externally. It is not a stack directly for mobile development, but a stack for rapid application database development. Virtually all applications use a database for the data, this takes the concept to the next logical leap. I've been doing database development for 30 years, and this technology stack excites me. You might compare it to Clarion, Filemaker or even Access (but way better). Designed from the ground up to have per field/row/screen security & auditing, mobile first, charts, reporting, etc. If you company needs any type of desktop/mobile application built let me know, this stack allows us to build it a lot faster meaning we can save you a lot in development costs. It doesn't matter if what you need is as simple as a contact database to something as complex as something that can fully run every part of your company. It scales and allows tying of different data sources very seamlessly.

In addition, if you have existing Access databases you want to convert to be modernized and intranet facing. I'm already pursuing working on a Access DB conversion to allow your app to hopefully seamlessly transition into this platform with little or no changes. I'm not opposed to writing converters from other platforms also, feel free to hit me up if you have some internal company apps you want modernized.