Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS code protection #1093

Closed
rborn opened this issue May 1, 2015 · 40 comments
Closed

JS code protection #1093

rborn opened this issue May 1, 2015 · 40 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@rborn
Copy link

rborn commented May 1, 2015

Hi everyone,

Is there any plan to encrypt the javascript code in production mode (release for AppStore)?
I know that right now it can be obfuscated, but this it's not enough :)

Thank you.

@nicklockwood
Copy link
Contributor

You could do this by using an encryption library to encode the JS and decode it at runtime before passing it to the bridge. The problem is that you'd have to ship the decryption key with the app code, so it would be trivial for a hacker to decrypt it if they were inclined to do so.

I'd advise against including secret information in your application, even in native code. Code on the client is not secret - secrets should be kept on the server.

@TheBrousse
Copy link

So what's the answer here? (omitting your personal beliefs and opinions)

Does the React project has any plans of protecting the developer's code or not?

@rborn
Copy link
Author

rborn commented May 1, 2015

@nicklockwood Thanks for the reply.

A determined hacker would get access to everything indeed, I'm talking more about "curious" people that might look into your house if you leave the door open ;)

Also the are plenty of cases where you would really need to store some stuff on the device starting with complete offline apps to apps that communicate with external servers which you don't control.
I know what ideally should be done, but in the real world things don't happen like this :) (Don't get me wrong please)

@vjeux
Copy link
Contributor

vjeux commented May 1, 2015

We're unlikely going to provide encryption or more sophisticated obfuscation.

Right now, all the module names leak and we're thinking of replacing them with numbers for efficiency reason, which should help a bit if we end up doing it.

@rborn
Copy link
Author

rborn commented May 1, 2015

But even with a more efficient code in place, the JS will still be in clear. Or am I getting it wrong?

@vjeux
Copy link
Contributor

vjeux commented May 1, 2015

That's correct, it probably won't fit your usecase.

We (as in Facebook) are not going to implement what you want anytime soon. That said, if you make the necessary changes inside of the codebase to have a pluggable way to inject an encryption/obfuscation plugin, that's something we may consider pulling in

@rborn
Copy link
Author

rborn commented May 1, 2015

@vjeux clear now, thank you 😄

@nicklockwood
Copy link
Contributor

As @vjeux said, we have no immediate plans to add encryption for JS bundle files, and yes, under the currently recommended bundling instructions, your JS will be included as plaintext that can be extracted and de-obfuscated with relative ease.

If you are just interested in preventing casual inspection of the JS source (as opposed to stopping a determined attacker) then I believe there is another way to bundle your code that will be much harder to crack (I've not tested this, so you'll have to try it and let me know if there are any problems):

  1. Compile your jsbundle file as normal
  2. Base64 encode the jsbundle file, then copy the contents:
  3. In your AppDelegate (or wherever you set up your RCTBridge/RCTRootView), add the following:
jsCodeLocation = [NSURL URLWithString:@"data:text/javascript;base64,AAAAAAAAAAAAA"];

Where AAAAAAAAAAAAA is your base64-encoded bundle. Then pass that URL into RCTRootView as normal to load your app.

The advantage of embedding the source code in this way is that by including it as a string constant in your app (as opposed to a text file in the app resources) it will automatically be encrypted using Apple's FairPlay DRM when you upload it to the App Store.

That means that anyone who downloads the app from the store and looks in the resources won't find the code, but more importantly even if they open the app binary in a hex editor they won't be able to find this base64 string and decode it because it will have been converted into encrypted gibberish. That makes it about as secure as ordinary native app code.

A determined attacker could still open the app on a jailbroken device and use some tools to copy the decrypted binary out of RAM, but as I said earlier, there's really no practical defence against that. This approach will certainly push the difficulty out of the realm of "casual curiosity" though.

Let me know if this works for you (or doesn't). It should be relatively easy to modify the packager to automate this if there's significant interest in doing it.

@rborn
Copy link
Author

rborn commented May 1, 2015

@nicklockwood this should be good enough for a start, I'll run some tests and let you know. Thanks a bunch!

@shayne
Copy link
Contributor

shayne commented May 2, 2015

You may want to test that the string in the binary is truly encrypted. I can't point to any mach-file docs (mobile atm), but in my experience large clobs are concatendated to the binary and are completely unencrypted. To test this download the App Store version of your app and run strings on the binary.

@jaygarcia
Copy link
Contributor

For the record, thousands of Cordova apps are shipped to the app stores with all resources unencrypted.

Just saying...

JG

@moduscreate

:: sent from my mobile device ::

On May 1, 2015, at 21:25, Shayne Sweeney notifications@github.com wrote:

You may want to test that the string in the binary is truly encrypted. I can't point to any mach-file docs (mobile atm), but in my experience large clobs are concatendated to the binary and are completely unencrypted. To test this download the App Store version of your app and run strings on the binary.


Reply to this email directly or view it on GitHub.

@rborn
Copy link
Author

rborn commented May 2, 2015

@shayne thank you. I'll keep this in mind.

@jaygarcia For many apps this might be a "nice to have" however for most of my projects the code protection is required :)

@robertjpayne
Copy link
Contributor

Chiming in here as I've done quite a bit of work around license key protection and sorts. Please realise no matter how much effort you put in to "securing" your JS code or secrets in an iOS client app a determined attacker will always get what they want. From what I've read from other developers it's about a 10:1 effort. That is for every 10 hours you spend "protecting" your stuff it takes an attacker approx 1 hour to reverse your efforts.

Trying to keep the source private is pretty moot-point since it's very easy to monitor some of the Objective-C code that react-native uses.

The best thing I recommend to anyone is to ensure your jsbundle has a signature and you verify that signature before you let it be loaded into the bridge. This is especially important for anyone delivering updates over HTTP/HTTPS.

If you are heavily worried about code protection use heavy obfuscation and RSA public/private keys to encrypt the source. Again anyone with a jailbroken device and a bit of time can easily latch LLDB onto the RCTBridge methods and snag your code.

@moduscreate
Copy link

Thanks Robert!!

JG

:: sent from my mobile device ::

On May 3, 2015, at 05:05, Robert Payne notifications@github.com wrote:

Chiming in here as I've done quite a bit of work around license key protection and sorts. Please realise no matter how much effort you put in to "securing" your JS code or secrets in an iOS client app a determined attacker will always get what they want. From what I've read from other developers it's about a 10:1 effort. That is for every 10 hours you spend "protecting" your stuff it takes an attacker approx 1 hour to reverse your efforts.

Trying to keep the source private is pretty moot-point since it's very easy to monitor some of the Objective-C code that react-native uses.

The best thing I recommend to anyone is to ensure your jsbundle has a signature and you verify that signature before you let it be loaded into the bridge. This is especially important for anyone delivering updates over HTTP/HTTPS.

If you are heavily worried about code protection use heavy obfuscation and RSA public/private keys to encrypt the source. Again anyone with a jailbroken device and a bit of time can easily latch LLDB onto the RCTBridge methods and snag your code.


Reply to this email directly or view it on GitHub.

@nicklockwood
Copy link
Contributor

@rborn did you get a chance to try my suggestion? Any problems?

@rborn
Copy link
Author

rborn commented May 5, 2015

Sorry, I'm traveling this days and I'll be able to touch the code only the next week.
But as soon as I'm back I'll test it and let you know :)

Sent from my iPhone

On May 6, 2015, at 12:37 AM, Nick Lockwood notifications@github.com wrote:

@rborn did you get a chance to try my suggestion? Any problems?


Reply to this email directly or view it on GitHub.

@rborn
Copy link
Author

rborn commented May 19, 2015

I'm really sorry for the delay.

@nicklockwood I did what you suggested (base64 the jsbundle) and the test app works ok (deployed both on sim and device).

However after I generated the ipa for appstore a strings command shows the base64 code in clear (as @shayne indicated).

@nicklockwood
Copy link
Contributor

Ah, pity.

The next option would be to encrypt the base64 URL and then decrypt it before passing to the bridge then. Not very much harder, but still an extra step. Doesn't really matter if the string is stored in the app or in a file.

@nicklockwood
Copy link
Contributor

Wait, when you say generated ipa for app store, do you mean you submitted it to Apple and then downloaded in iTunes once it was approved and released, or did you just export a release build from xcode?

The encryption is applied by Apple in the cloud, so it doesn't encrypt the ipa before you actually submit it.

@rborn
Copy link
Author

rborn commented May 19, 2015

Oh, didn't know this, I thought the encryption is done locally.
But if it's like this, why the jsbundle files are not encrypted? (found that in a react-native app that's in the Appstore).

@nicklockwood
Copy link
Contributor

Fairplay encryption is only applied to certain parts of the app binary itself, not to assets in the resources folder (they unzip your ipa, add the encryption, and then reassemble the ipa)

@rborn
Copy link
Author

rborn commented May 20, 2015

I see, thank you

Sadly I cannot test right now with an app in appstore 😞

@brentvatne
Copy link
Collaborator

Very interesting discussion, good to see some options presented here. Given that this is outside of the scope of React Native itself, let's move the discussion to discuss.react.js.org if there's anything left to be said!

@rborn - feel free to ping us again in this discussion with followup if you do get a chance to test it in the app store!

@thngdude
Copy link

Is there a recommended tool to use for obfuscation of react native javascript code? Can I use something like Uglify?

@sudarshanJagtap
Copy link

I tried the Base64 encoding of jsbundle file in iOS, and it works great.
But having problems on android.
How do the same on android jsbundle.

@tlcheah2
Copy link

tlcheah2 commented Sep 3, 2016

@sudarshanJagtap Hi, maybe I know how to do the Base64 encoding of jsbundle file in iOS ?

@ACCTFORGH
Copy link

Can their be some directions to the community as to how to implement obfuscation or alternative security measures in the js bundle?

@ide
Copy link
Contributor

ide commented Oct 18, 2016

The JavaScript is already obfuscated with the minifier. The security (not sure if that's the right word...) is comparable to a website's. RN's packager already ships with that.

@jesucarr
Copy link

+1 to automate the jsCodeLocation trick. Is there anything similar for Android?

@kesha-antonov
Copy link
Contributor

@rborn @brentvatne
Hey guys!
Can you please tell how to do "Base64 jsCodeLocation" in details? Or how you did it?
I'm using RN 0.40 and have not enough knowledge in C for now to do it myself.

@kesha-antonov
Copy link
Contributor

@sudarshanJagtap @jesucarr Hey guys! How you did base64 "in details"?

@anton-matosov
Copy link

I have implemented embedding of jsbundle in #12587

@chatras
Copy link

chatras commented Mar 25, 2017

For someone trying to figure it out. Here are the steps:

  1. Get app bundle from localhost and save it as index.ios.bundle
    http://localhost:8081/index.ios.bundle?platform=ios&dev=false
  2. Encode file (in terminal)

    openssl enc -base64 -A -in index.ios.bundle -out main64-1.jsbundle

  3. (Optional)
    Decode for testing (in terminal)

    openssl enc -base64 -d -A -in main64-1.jsbundle -out main64-1-dec.jsbundle

  4. (Once)
    Updated files on ios react library based on this pull request (this can be found in your xcode project). Implement embedding of jsbundle as base64 data string to enable AppStore encryption discussed in #1093 #12587
  5. Make JSBundle.h and paste encoded data in it
    #define JSBUNDLE_BASE64 @"[PASTE ENCODED DATA HERE]"
  6. Update app AppDelegate.m
    #import "JSBundle.h"
    jsCodeLocation = [NSURL URLWithString:@"data:text/javascript;base64," JSBUNDLE_BASE64];

@leizeQ
Copy link

leizeQ commented Apr 5, 2017

I'm having problems with static resources. After using this process, all my images are gone from release app.
I am not sure where to put the "assets" directory so that react-native can find them.

If I understand it correctly, RN looks for the "assets" directory in the same folder as "main.jsbundle". But now I don't have main.jsbundle file.

@chatras
Copy link

chatras commented Apr 5, 2017

Yeah same problem here. Also the the loading of the js code is very slow now. Used to take 3 seconds, now its over 10s.

@jaygarcia
Copy link
Contributor

@chatras Are you testing this in a prod build?

@jsdario
Copy link
Contributor

jsdario commented Apr 20, 2017

How should we use getJSBundleFile in Android in order to load and decode the base64 version?

@anton-matosov
Copy link

In Android it doesn't make any sense as strings are not encrypted upon binary download. Additionally there were problems with loading resources and performance with base64 approach.
After all even Apple's strings encryption is fairly easy to crack with jailbroken device. So it is better to obfuscate code, it is proven way to make it harder (not impossible) to read.

@nitinsoni9235
Copy link

nitinsoni9235 commented Jun 23, 2017

Hi @chatras , @nicklockwood ,

I am trying to use this code but its not working.
jsCodeLocation = [NSURL URLWithString:@"data:text/javascript;base64," JSBUNDLE_BASE64];

I don't think. its decode the JSBUNDLE_BASE64. can you please explain meaning of this line.

@anton-matosov
Copy link

@nitinsoni9235 first thing first, don't do it. It's a false sense of security. More details here #12587
Second it will slow things down, increase memory usage and will break loading if images from resources.
If you really-really want to give it a shot, try code form this PR #12587

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests