spliit-app / spliit

Free and Open Source Alternative to Splitwise. Share expenses with your friends and family.
https://spliit.app
MIT License
1.17k stars 185 forks source link

Feature Request: encryption at rest #34

Open manuerwin opened 10 months ago

manuerwin commented 10 months ago

First of all, thanks so much for making this app <3 I would like my shared expense data to be encrypted when stored in the database (either via your hosted implementation or my own).

I’ve had a newb scan of the code and can’t tell if the data stored is encrypted?

if it’s not, how about few fields as possible, perhaps the following?

With an optional passcode/phrase entered on group creation (or later) that encrypts and decrypts the above information for the viewing/interacting user.

The passcode/phrase is shared along with the group url (ideally separately 😅), and entered by a user when using the app (not sure how often though?).

I can certainly have a go at introducing this, however may take me awhile :)

ChristopherJohnston commented 10 months ago

if encryption/decryption is done at the client side then I think local storage of the key along with relevant encrypt/decrypt functions would be sufficient to satisfy this feature

manuerwin commented 10 months ago

Thanks @ChristopherJohnston for replying! Like and agree with the client doing the heavy lifting re: encryption/decryption. Do you have advice on how long the key should be retained in localstorage? or should it be in sessionstorage?

scastiel commented 10 months ago

It would be possible to end-to-end encrypt group names, participants names and expense titles, but that would mean having to enter a password the first time accessing the group. The password could be stored in local or session storage, and could even (optionally) be part of the shared URL.

With a different storage implementation, it would also be possible to encrypt all group information. Since there is no shared information between groups, it wouldn’t be for a group data to be stored in an (encrypted) JSON object. This implementation would require dealing with conflicts (maybe with CRDTs?) in case two people update the same group (e.g. add an expense at the same time).

manuerwin commented 10 months ago

Thanks Sebastien!

I was thinking the simpler version to start as it felt like group names, participants names and expense titles are the data that are “identifiable”.

I’ll have a go at this unless someone beats me to it, may take a few weeks though :p

ChristopherJohnston commented 10 months ago

I had a little play around with the cryptography functions from libp2p, using a local IndexedDB to store the private keys

https://github.com/ChristopherJohnston/libp2p-keychain-encrypt-example

This could be a little bit too much and a lighter solution more appropriate, but I see this working as follows:

getKeychain(passphrase).createKey(groupId, "rsa", 2048)
getKeychain(passphrase).exportKey(groupId, passphrase)
getKeychain(passcode).importKey(groupId, privateKey, passphrase)
const cms = new CMS(getKeychain(passphrase));
const encryptedValue = cms.encrypt(groupId, uint8ArrayFromString(value, 'ascii'))
const cms = new CMS(getKeychain(passphrase));
const value = cms.decrypt(encryptedValue)

Potential problems:

ChristopherJohnston commented 10 months ago

POC of the above here: https://github.com/scastiel/spliit2/pull/41

scastiel commented 10 months ago

Thanks @ChristopherJohnston I think your implementation makes total sense!

If the private key and passphrase are lost, all data is lost

I’m especially worried about this point. I’m afraid it’s hard to let users know that they have to store or remember the passphrase, or the full shared link, somewhere, otherwise no one (even me) will be able to restore access to their group. Many people have trouble with this idea.

An alternative could be to let users decide wether or not they want to encrypt their data, with the advantages and drawback of both options. Again, it would require a bit of clear explanations, and to maintain two distinct logics.

manuerwin commented 10 months ago

Very much agree with making encryption optional with clear warnings/suggestions (e.g. use a password manager plus regularly backup transactions).

Pt4r commented 4 months ago

Are there any news regarding this issue?

manuerwin commented 4 months ago

I’ve not had a chance to look at this yet, hoping to do so next month.

david-alm commented 4 months ago

Hey Spliit. Really love the application, it's already come in handy a couple of times. I've read through the thread and wanted to recommend of minibone for this usecase. P.S. For transparency, I'm a co-author, so take my opinion with a pinch of salt.

The library is restricted to only using symmetric encryption (which would be faster than the implementation used in #41) and has a bunch of additional benefits for this particular usecase (like seamlessly handling key rotations). As @scastiel mentioned, the ideal way to derive the password would be to optionally include it in link's fragment or ask users to store it in their password managers.

Happy to chat and/or author a PR if you're happy with the approach.

manuerwin commented 3 months ago

@david-alm I'm a newb in this space, great to see minibone is built atop the Web Crypto API and agree symmetric key encryption will work well here. Please please go ahead with your PR if you have time :) If not, I'll continue my slow learning journey :p

raymondji commented 1 month ago

Just for my understanding, does Vercel Postgres not already encrypt data at rest?