3

I'm developing application with react and all the main logic is on the client side. I want to force users to use the application only if they paid for the app subscription.

Are there any methods to prevent(or harden the ability to remove certain restrictions/api calls etc) users to access the app without paying? I was thinking of authentication obfuscation, to make it hard to delete a simple check of auth(if authenticated - proceed, otherwise - block), but this is very simple. Are there anything else I can do / you can suggest?

Thanks

  • 2
    Does this answer your question? [Obfuscating JavaScript code](https://security.stackexchange.com/questions/35828/), [Securing clients side scripts](https://security.stackexchange.com/questions/3174/), [Hiding JavaScript source code](https://security.stackexchange.com/questions/30928/). – Steffen Ullrich Jan 10 '21 at 10:08
  • @SteffenUllrich not at all, I'm aware of obfuscation(and also about security through obscurity) and I am going to use it. What I'm looking for is another solutions to make it harder to crack – mikereverichi Jan 10 '21 at 10:15
  • 3
    The last question I've linked to is more generic and clearly states *"Javascript code executes on the client browser, so the client browser sees the code, and __every user can obtain it. At best you can__ obfuscate the code ..."*. - So, there is not something better. Don't execute sensitive code on the client side but execute at only on the server side where it is hidden from the browser. – Steffen Ullrich Jan 10 '21 at 10:23
  • @SteffenUllrich I know exactly what you're talking about. I'm aware of how JS works on the client side. Additionally to obfuscation there's an authentication calls(even via lan), for example. I am going to use it, but I see it as not enough and look for additional ways to make it harder to crack – mikereverichi Jan 10 '21 at 10:49
  • By the way, just for clarification, is this a react web app (i.e. ReactJS) or a React Native app? – nobody Jan 10 '21 at 11:03
  • @nobody react js – mikereverichi Jan 10 '21 at 11:08
  • @mikereverichi Ah then my previous comments under my answer about looking into hardware solutions are not applicable at all. – nobody Jan 10 '21 at 11:11
  • What does the app do? Does it interact with backend services or work independently? – Conor Mancone Jan 10 '21 at 13:30

3 Answers3

9

No, there isn't any foolproof way for securing your code client-side. This is essentially the same problem that DRM has tried and failed to solve. You can obfuscate your code, but that will only make it more difficult to reverse-engineer. With sufficient skill and effort, someone will eventually be able to crack it. And the crackers only have to win once - as soon as one person manages to bypass your client side checks, they can publish it on the internet and everybody else can profit from it (except you of course :).

The solution is simply to move (at least some critical parts of) your code server-side.

nobody
  • 11,251
  • 1
  • 41
  • 60
  • Yes, I know that. The purpose is to maximize the initial effort – mikereverichi Jan 10 '21 at 09:25
  • @mikereverichi Then I don't quite see what your question is. Are you asking if there are any techniques than obfuscation to protect your code? Perhaps hardware based solutions *might* help. Or are you asking for obfuscation techniques? That would probably be off-topic for Security.SE – nobody Jan 10 '21 at 09:35
  • I know about obfuscation techniques and I am going to use it, I indeed try to find another solutions that potentially can harden the client side for the purpose mention in the topic of this question. What are the hardware based solutions are you talking about, can you please elaborate on this too? – mikereverichi Jan 10 '21 at 09:48
  • I am looking for a solution to prevent cracking the application. I'm aware there's no 100% way to make it, but there are probably many ways to make it harder to crack. I am looking for this ways. – mikereverichi Jan 10 '21 at 10:18
  • 3
    @mikereverichi: There is obfuscation and there is better obfuscation. As long if you don't have any control of the client system that's essentially all you have regarding protecting code which runs on the (untrusted) client side. Thus, don't run the relevant code on the client side. – Steffen Ullrich Jan 10 '21 at 10:28
3

You cannot prevent reverse engineering of the client code. You can obfuscate it and make it harder for many users. But if somebody sees that the benefits of reverse engineering are higher than the costs needed for it, then they will of course reverse engineer it and implement work arounds that they need.

If somebody works it around just to circumvent some restrictions or to get some payed services for free, this is only one problem.

Another important aspect is security. Even if users don't want to break your code for their own benefits, it can be attractive for attackers. For instance, one of your users may decide to attack some other users. First they would reverse engineer your code. Depending on your logic there can be a possibility of impersonation. I.e. the attacker can execute some logic in the name of other users: The attacker can send requests to your server and obtain some sensitive data of other users, modify the data of other users, execute some important operations in the name of other users. You cannot prevent it.

That's why I'd suggest you to change your approach as follows:

  • Implement all the logic on the server
  • On the server don't trust client and validate every request:
    • Check if user is authenticated
    • Check if the user has permission to execute particular operation
    • Check if the data in the request are valid
    • Etc.
  • Duplicate some logic on the client only if it improves usability

For instance, user wants to spend $1000, but has only $900 on the account. You should always check this on the server side. But if the check is simple, you can duplicate it on the client side. In such case is user enters 1000, you can display validation message immediately, without sending request to the server. But if user breaks client code and sends 1000 to your service, your service should not trust the client request and should perform all validations needed in your business logic. Thus requests from malicious users will be processed safely in any case and the friendly users will get a better performance.

I suggest either not to duplicate the logic at all, or to keep the amount of such logic on the client side as small as possible, because each time you change or extend the logic on the server side you will have to adjust also the logic on the client.

mentallurg
  • 8,536
  • 4
  • 26
  • 41
  • Thank you so much for spending so much time explaining all of this. I'm aware of everything you said, this isn't about it. Main logic is not all the logic, especially when we talk about payments. Thanks once again – mikereverichi Jan 10 '21 at 12:48
  • @mikereverichi: It does not have to be about payments. It can be anything important to you. If it were not important, you would not try to prevent users from modifying the code. Since you to try to protect, then it means for some users it will have sense to break it and some will do that. There is not magic solution for that. – mentallurg Jan 10 '21 at 12:52
  • Imagine an encryption that is done purely on the client side with "dumb-server". The application I'm developing is very similar to that approach, though not exactly. I can't, simply can't move main logic to the server. And once again, I'm aware of what you're talking about and would agree in every other case with you – mikereverichi Jan 10 '21 at 13:02
  • 2
    @mikereverichi: Why did you ask a question if you already knew the answer and you only want *reject our reality and substitute your own*? – Esa Jokinen Jan 10 '21 at 14:49
0

Consider splitting your React application into several SPAs. Each SPA is rendered by its own script bundle. For example you can have Login SPA rendered by login.js bundle and the rest of the application catered for by the App SPA rendered by app.js bundle.

The first bundle would allow users to login. After that their identity is established and the payment status is retrieved from a database. The link to the next webpage with the paid-only functionality will be conditionally shown to paid users, otherwise the link will be hidden but still easily discoverable by inspecting HTML via Page Source browser's menu. Following this link will make the webrowser load the second SPA which calls the API endpoints for the paid users and contains webpages restricted to the subscription.

Are there any methods to prevent(or harden the ability to remove certain restrictions/api calls etc) users to access the app without paying?

The webserver will refuse to serve the app.js bundle in case of a non-paying user.

As a practical example of a React app split into two SPAs with two script bundles see demo website and the Crisp React solution. The demo does not make the second bundle download conditional but this functionality can be easily added.

P.S.

@winwiz1, do you mind to explain how is this different than splitting the code with protected routes?

I assume it's about the difference between obfuscation and bundle/URL rotation.

Obfuscation

Doing it once is not a reasonable option. Someone can customize an existing deobfuscator or write a custom one and post a hack on some forum. You can keep changing the obfuscation technique. If you do it cheaply (for example change the constant used to XOR the ‘protected’ code), then a decent deobfuscator embedded into a hack can pick it up automatically. If you do it in-depth then continuing doing it over and over again would be expensive and likely not a viable option.

Bundle/URL rotation

Financial institutions exchange nightly batch transactions using encryption with complimentary hardening achieved by making URLs non-public. And they enhance security by rotating encryption keys. In your case the URL is public so you could achieve/enhance hardening by rotating URLs.

For this to work, the client-side caching of the HTML (that contains references to the script bundles) will have to be disabled, as is the case with the demo website and the solution mentioned above. The solution already uses bundle names that include hashes and generates HTML automatically. The only outstanding task would be to rotate URLs by inserting some GUID into it.

The difference

The difference will become obvious if you try to estimate the costs (time and efforts) required to implement obfuscation vs bundle/URL rotation, though this requires a good grasp of both approaches. The next step would be to pretend to be a hacker and consider the practical solutions aimed at defeating obfuscation or bundle/URL rotation. For each solution consider the implementation with related complexity, costs and risks. Which would obviously depend among other things on the method of supplying the non-paid users with the hack, whether it’s once off posting to an underground forum or requires to additionally have continuing online presence - to keep logging in to get new bundles with rotated URLs, to keep making these available to non-paid user base via some online notification mechanism utilised by the distributed hack etc.


The whole issue is about authentication (and getting user identity with payment status) folowed by authorization (whether to expose some functionality or not). As already noted by others, it's a wrong approach to try to enforce either on the client side. The enforcement should be on the server side complimented by additional security measures on both sides because good security is always multilayered.

winwiz1
  • 101
  • 3
  • This doesn't prevent a paying user from publishing the source of `app.js` so other people can use it for free – nobody Jan 10 '21 at 11:05
  • Thanks for helping, but I don't see how this is different than simply splitting the code(routes) of the original application. This is also won't make any sense once the user obtained the code and as a result all the process is useless to start with. I'm not even talking about it makes it more ugly for development. – mikereverichi Jan 10 '21 at 11:06
  • 1
    OP has asked how to 'prevent' or 'harden' - which presumes there is an initial understanding of the difference between those two terms. Prevention is about creating an inability to access some functionality. Hardening is about making someone spend a bit more time in order to access it. Splitting into several SPAs won't 'prevent' but it will 'harden'. This could be further hardened by having daily automated process which produces `bundle..js` with different hashes refernced in HTML. Bundles could call API enpoints with URL having other random patterns, other hardening technicalities. – winwiz1 Jan 10 '21 at 11:24
  • @winwiz1, do you mind to explain how is this different than splitting the code with protected routes? – mikereverichi Jan 10 '21 at 12:43