Security is never perfect, the minute the device is under the control of an attacker; you have issues. So the primary goal of security is to raise the bar so high; that it becomes much more profitable to attack your competitors app then your app. Don't make your app the easy target.
So, recently I have seen several blog posts and even a podcast in the NativeScript community on Mobile application security. Unfortunately; some of the advise is just wrong. I really hate to be harsh; but giving out bad advise is far worse in my opinion, than giving no advise when it comes to security of your application and infrastructure.
Lets talk about the Good, Bad and the Ugly... Lets start with the Bad.
Bad Advice
Would you really accept someone saying it is super secure to set a cookie to "isAdmin=1" for allowing admin access. Well, I'm here to debunk several things that I've seen show up recently as they "sound" good, but are still bad advice in most cases.
First, in a couple blog posts I read, and the pod cast I recently listened too, all recommend using NativeScript-Secure-Storage for storing secure local data. Simply put you are just as safe using my NativeScript-LocalStorage plugin (which has no security at all) as the Native-Secure-Storage plugin in all cases that I can think of except one very specific case 1. (So basically; you have added no extra security to your app in almost all cases to just using raw file storage on the platform.)
How is that you ask? Its encrypted storage using industry strength encryption systems. Well, the issue is that any person attempting to pry the secrets out of your app; will be running it on a rooted phone (iOS or Android, doesn't matter the OS). With a rooted phone, I now have full access to EVERYTHING on the device including the secure "storage" files. Bam, game over; do not pass go! I now have your secrets that you thought you "securely" stored. As you can see a pretty pointless plugin in all reality for securely storing anything. ( This is a bit of a simplification, but for someone who knows exactly what they are doing -- they can trivially pull all the values out of the secure storage plugin in literally only a couple minutes on any rooted device. )
(1) - Using JSScrambler or AppProtection can mitigate most of this issue, there are still some corner cases that might still allow access to all the data even using these products.
API Endpoints and Keys
The next thing I heard talked about was HTTP, Kinvey and API keys; the advise wasn't bad so much as incomplete. This isn't specifically against NativeScript 4, (or Kinvey), because really React Native, Titanic 2, Phonegap/ionic/Cordova 3 , and several others are all JS code based; the JS code is shipped with the application; meaning the code can be fully read by your attacker. So when you have in your plain JS code:
Kinvey.init({
appKey: '<appKey>',
appSecret: '<appSecret>',
encryptionKey: '<encryptionKey>'
});
or
const result = http.get("https://master.technology/secretApiPath?appKey")
Guess what I now have access to? I have access to the ability to read (& write?) any data your appKey has permissions to do. So it is very critical to initially assume this code is 100% readable and that none of this is secured at this point. So make sure you plan the usage rights for all your keys and api endpoints properly. If you don't plan for this, then your appKey
may give me access to read and write things that even the app doesn't do, which could be really bad for your overall security footprint.
Again, I want to stress this is NOT specifically talking about NativeScript; as this same advise applies to React Native, Cordova, Titanium, Flutter, Java, ObjC, and pretty much every environment. If I can get the API keys out, I then have the full access that your key offers. Now JavaScript based systems are tremendously easier to pull this out than say; something using compiled code (like Flutter or ObjC). However, you know what I would start with on a 100% native app? I will just run strings on it... Its amazing what is easily seen in plain text from a compiled applications like all your endpoints and keys.
(2) - Titanium has a built in encryption of the source code. However, last time I looked it was the same key for all apps; once you have the key (which of course I do ☺) -- it is trivial to decrypt all the code in every single Titanium app. But I will admit it did take a bit of time to figure out the key, so it isn't bad system.
(3) - Cordova has a third party plugin to encrypt the code; I spent less time breaking it than I did downloading it. To say the least it was the simplest of the bunch.
(4) - NativeScript HAD an official plugin, after pointing out how easily I could break it; it was dropped shortly after. I believe you can still find the repo for it -- and you can probably still use it; but it really is as trivial to break as the Cordova plugin.
Encryption
So in this case; any api keys and important endpoint urls; are better off encrypted in the app. That won't stop everybody; but it will make the bar to get the values out a lot harder. This applies to ALL app types! You want to encrypt or obfuscate the data and do not make it trivial for them to find it. If your JS code says:
let apiKey = "##DS#F#1%F#aD#D#";
apiKey = decrypt(apiKey);
Guess what code I'm going to look at next? So using the decrypt
right next to the assignment is not going to slow me down in the slightest. Now using it somewhere in the middle of something completely unrelated might actually make me miss it, especially if it is not called decrypt
but something like initializeCameraModule
. So if the value you stored in your apiKey is not a real apikey but a obfuscated key and since I am unaware of it, you cause me a lot of wasted time as I'm trying to figure out why my external calls to your api aren't actually working properly. Again, wasting my time is the quickest way to make me want to attack a simpler or softer target. Now someone paid to figure this out; will figure it out; but a casual hacker might give up. Each barrier that you can raise and actually waste a bit of time 5, will discourage and drive up the costs to figure it out.
Another partially bad advise is the use of Encryption to keep your data at rest in a state that it is not easily retrieved. It sounds great! But this suffers from the exact same issues as discussed above. If I have easy access to your source; I'm going to trivially find your key and then decrypt all the data. This exact same warning also applies to my own encrypted SQLite product; if you don't protect the keys; I can pull all the data out.
So doing something to hide the keys is critical.
One potential solution if your app has a login, is that when the user types in the password for login it is used to calculate a new encryption password, which is then used to access the encrypted data store or sqlite database. Then, since I, the attacker don't know the password, I have no ability to ever retrieve the data from any data at rest no matter what I do unless I convince you to give me your password.
(5) - Any place you can seriously waste the person attacking your app's time is pure gold. Eventually they will figure it out; but in the meantime you drive up their costs and frustration. At some point, if those costs are significant enough, they will stop and attack an easier target. Their are many other techniques that you can do while working with keys/endpoints that can waste significant amounts of time and should be considered.
Compiled Code
In some cases it might be in your best interest to have compiled C code that sets up certain values and/or decrypts parts of your data. This makes it a lot harder to figure out what is going on, and can store keys you want protected. However, if your source code is still plain text, then I can easily see how/when you are calling it, add breakpoints and then determine how it works. So this really again can add no real security or can be an extra high barrier depending on what other protections you have in place.
Source Code
There are two things about the source code in JS based apps that you need to consider.
- The first is that any secret apis, keys, endpoints and your proprietary business logic are easily found.
- The second, which is related but a different aspect which might concern you more; is that I now have all your source code. I can easily create a clone of your cool app on the app stores with literally no development costs (new startup screen, some css color changes) and then totally undercut your price.
So lets look at some ways to try and mitigate the primary issue of visible source code...
The Ugly
A lot of people believe Obfuscation is a good and a valid answer. Since it is frequently put forward and considered a valid solution; and so it is recommended; this is why I consider this the Ugly.
Obfuscation
Now one thing that they got correct in the blogs & podcasts; was that obfuscation actually does very little. In fact other than JSScramber, the rest of them that I've played with are literally only one step above useless. If they minify your code; they have some usefulness as your app size goes down. If they slow your app down, then they are actually worse than worthless. But odds are very likely they will not have any performance hit. Unfortunately, almost every single one of them, by dropping the code into a decent beautifier, will eliminate almost all obfuscation. So using obfuscation (which includes the recommended NativeScript --uglify flag you see in some posts) really presents absolutely no additional security beyond the minification of making myFavoriteVariable
become c
. That can eliminate some really basic understanding. But an average JS developer can easily still read the source code. If I'm the one breaking your code; all I'm going to do is just drop the newly beautified code in phpStorm and refactor any variables as I figure them out and then we will be right back at pretty close to your original code rather quickly.
It can slow the attacker down some, but really it is something that won't really cost that much time as refactoring and code insights will allow me to trace things down rather quickly. So in my opinion; this really offers very little value. In addition most Obfuscation will NOT change any of your static strings. So if you had:
let apiKey = "xyz"; <br>http.get("someurl" + apiKey)<br>
and it transforms it to:
let a="xyz";http.get("someurl"+a);<br>
Does that really stop anyone from understanding that a
is probably the apikey?
In addition, just a fyi -- in all cases (except JSScrambler) of obfuscation that I have seen and played with I have broken the obfuscation is less than 30 minutes, most the time in a couple minutes just using a beautification will do it. But there were a couple that actually required a few minutes to read through the output to figure out where to attack it at.
The Good
Lets discuss the best ways to potentially handle the the security of your application, lets look at the two possible ways to handle this..
JSScrambler
JSScrambler actually provides really good Obfuscation at the higher levels. But for the highest quality obfuscation your app pays a significant speed penalty constantly; like up to 50% slower depending on what the code is doing. So this is one of those things you have to evaluate if the added security is worth the performance hit and choose the security level you are most comfortable with. On some of the lower settings, there is no constant performance hit; but it is a lot more trivial to eliminate the obfuscation. This is one area you can increase the bar extremely high. Which would potentially not make it worth the time to attempt to figure out your app, but their is a serious price to pay. Now I haven't heard of anyone that has actually hacked JSScramber set at the highest level; but do you really think anyone would tell you if they could? ☺ Please feel free to pay me to find out how secure it is. ☺
Encrypted Source Code
There is one other service AppProtection.net that offers encrypting the source code. This actually is my product/service; so major disclaimer here!!! I think this also raises the bar extremely high . With AppProtection; I encrypt all app JS code and the V8/JSCore engines decrypt the files while running. All my tests had ~10% slow down during the loading of encrypted code as the code had to be decrypted in memory. (No decrypted code is save to disk at any point, it is always only in memory), so each time you start the app you have a perf hit. But you do not have the "constant" hit as with higher levels of JSScrambler, since the code is running from memory will already decrypted. However, as with anything; someone extremely skilled in debugging can eventually even break this also.
So in all reality the only real solutions at this moment to actually protecting your source with NativeScript is JSScrambler or AppProtection. With other products like React Native or Cordova, JSScrambler is the only current solution. (Please note; if I get enough requests for React Native, I could bring the AppProtection tooling to it as the code already runs on JavaScriptCore)
So now that we talked about the Good (Source Encryption), the Bad (false information that doesn't help) and the Ugly (Obfuscation). So, lets discuss a couple related items..
SSL Pinning
Using SSL Pinning can be very valuable for the opposite side of this; making sure your app is actually talking to your server. This will eliminate Man-in-the-Middle data watching. So if you are transferring data that you value; you might consider adding SSL pinning to your app. However you do have to make sure that you plan for how you will update the key with the new SSL key when it expires.
Security in General
I am going to re-iterate; their is NO PERFECT security, all security can be eventually broken. It is impossible, once the app is in the attackers hands to stop them. Just look at how frequently Denuvo gets cracked -- they are paid millions to get it correct and are constantly updating the security and it still gets broken frequently. So once an attacker has your app, they now have the ability to do anything I want with it. So the goal is to what we call security in depth. Do multiple things that raise the bar as high as you are willing to pay the price. As their can be a cost for each security measure you implement. For example if you are super paranoid; you could use AppProtection, JSScrambler, and multiple native C modules all in the same app. Then if the attacker finally broke one aspect of your security their are still several others blocking them from going forward. Now that is being ultra paranoid, but their is nothing stopping you from doing it.
Final Notes
If you are working on an app that needs to be secured and/or you want someone to do some security contract work on your app and/or the API endpoint infrastructure, we can assist in this. If you would like someone to attempt to break your app's code and suggest additional things to strengthen your apps security footprint; again please contact us at master.technology
About the Author
I am the owner of Master Technology, which focuses primarily on server side work and desktop apps. If you need any NativeScript work done; from optimization, code reviews, training, help on an app, creating plugins, and even full apps created. Please feel free to inquire at master.technology.