5

I'm developing an app in React that does encryption on the client-side and sends the encrypted content back to the server for secure storage (e.g. think of online-wallets). This works well since the data is secure in case someone malicious gets access to the data. However, in case a hacker gets access to the server and changes the actual React code to send the data back in raw format as opposed to encrypted, this would defeat the whole system.

So how can I force the React app to cache only once, and then any time in the future, before it pulls a new version of the app, it will warn the user that "a new update is available and that they need to check twitter and/or GitHub to ensure validity"?

Basically I need to detect "cache invalidation" attempts before they happen and warn the user. How could I do this?

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
Muppet
  • 5,767
  • 6
  • 29
  • 39
  • 2
    That's a good use case for a [service worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) – Jake Sep 02 '20 at 14:30
  • If you created your app using Create-React-App, there should already be a serviceworker.js file in your src directory – Jake Sep 02 '20 at 14:31
  • it is, and yes, I want to use service workers, but there is no way to protect from updated content, is there? I want the user to be able to say "don't update, even if there is new content", because it could potentially be dangerous – Muppet Sep 02 '20 at 23:58
  • I haven't worked with service workers much myself, but since they are a sort of cache controller I think you should be able to figure something out... But maybe it's better if you just try to make your server protected against remote code execution attacks... – Jake Sep 03 '20 at 11:01
  • agreed, but the thing I am designing would allow anyone to spin up their own server - so client sided security needs to be a possibility – Muppet Sep 03 '20 at 13:54
  • As I understand you store encrypted data on the server? Do you have any other sensitive data? Do you have a separate highly secured circuit on the server for validation of data from the clients? To be sure that data is encrypted? – Alexander Alexandrov Sep 05 '20 at 14:42
  • Regarding custom caching. As I understand this is your case https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker#on_user_interaction – Alexander Alexandrov Sep 05 '20 at 14:45
  • 1
    Something about this feels off to me, like you're worrying if the the front end should really "trust" the back end. It sounds like you're trying to make caching part of your approach to resolving this trust issue. If you have cause to think your server is a big target, I'd suggest setting up a robust security system around that so you can monitor/detect intrusion. – pixelearth Sep 06 '20 at 04:08
  • 1
    Also I don't understand how caching would protect against the case when the app is loaded for the first time by a browser AFTER the server has been compromised. There's no cache to invalidate so it would trust by default even though it's compromised. – pixelearth Sep 06 '20 at 04:08
  • This is a fairly hard problem: basically you want to do what code signing does for mobile apps with manual updates. This poses multiple problems: - you need an independent and trusted source to get the code signature (which I assume you would display before updates) - you need explicit manual control over caching and ways to check the signature - you need to deal with old versions of your software running with potentially new servers. – Porcellus Sep 07 '20 at 08:55
  • My approach would be to provide a service worker simple enough that users could check it before first use (and set it to be never updated) and do offline code signing with the service worker checking code signatures in a custom header with a hardcoded public key. – Porcellus Sep 07 '20 at 09:03
  • Good ideas, what do you guys think about instead hosting the react (client side) part on GitHub Pages, where it's run live from that directory and everyone could see the current content? That means one could set a watcher on the GitHub repo and get a warning anytime it changes? At least that way people COULD be completely safe / have a warning, if they wanted to – Muppet Sep 07 '20 at 17:36
  • This doesn't make sense. If someone has access to your server, they can force a redirect to a malicious version. If the client is using a dependable browser, then they may get a warning about the redirect. You have a lot of factors and a lot of education to do if you want to implemented this. – SILENT Sep 07 '20 at 18:26
  • no, the server would be running on GitHub Pages, which would run directly from the visible source code in the repo. You would not be able to change the running server without changing the server code visibly in the repo. The point is not to protect everyone by default, the point is to allow users to be warned about changes on the server side, if they so choose to. A server running on immutable source code with a watcher on the repo seems to be adequate for that purpose. – Muppet Sep 07 '20 at 19:44
  • It depends on your trust model: do you trust GitHub (Microsoft)? Also, it provides no convenient way of checking if it was altered before getting to the user. (E.g. on the server where Pages is hosted, on CDN if you use any). If you are using any kind of transpilation that makes hand checking the code fairly impossible. – Porcellus Sep 08 '20 at 10:17
  • @Muppet I was referring to the concept where there is no server trust. With Github, you are trusting Github's security. If you are worried about the client losing control of their server, why not establish your own server that provides the react app and a database that can link to the client's cloud storage solution (ie Github, etc)? I believe there are services that already do this. Even if someone steals it, they won't have access to. However, I would be more worried they would delete it in the first place. There's a lot of bitcoin stuck in limbo. – SILENT Sep 08 '20 at 15:41
  • Is this not what TLS is for? If your server is signing requests and responses, then what comes from the server will be signed and be validated to be from a trusted source. I think you're over complicating this. Just enable TLS and use https for all requests. – Kyle Sep 08 '20 at 18:44
  • TLS protects the channel: the communication between the server and the user. If someone with access to the server decides to mess with your code TLS won't help (and render your E2EE partially useless). Offline code signing helps in this case because the attacker would have to have access to a private key that is easier to protect than a fully online server with many complex parts. We could go even further, but even this would mean that you only need to trust the dev (or the dev company) and not the hosting provider. – Porcellus Sep 08 '20 at 20:43

1 Answers1

2

A couple of options you might like to consider:

  • Set object.freeze() on your cache after the first time the data is loaded and then use try/catch to detect another attempt to write to it.

  • Create a Proxy of the object

const cache = Proxy({
  isSet: false,
  data: {}
}, {
  set(obj, prop, value) {
    if (prop === 'isSet') throw new ReferenceError()
    if (obj.isSet) throw new Error('Cache already set')
    obj.isSet = true
    return Reflect.set(...arguments)
  }
})
David Bradshaw
  • 11,859
  • 3
  • 41
  • 70
  • This is a good answer, but should be improved by adding comments from the main thread, i.e.: (1) Using GitHub Pages and thus relaying the problem of trust to github and (2) using Service Workers – Muppet Sep 09 '20 at 01:25