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

Feature Request: encryption at rest #34

Open
manuerwin opened this issue Jan 9, 2024 · 13 comments
Open

Feature Request: encryption at rest #34

manuerwin opened this issue Jan 9, 2024 · 13 comments

Comments

@manuerwin
Copy link

manuerwin commented Jan 9, 2024

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?

  • Group.Name
  • Participant.Name
  • Expense.Title

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
Copy link
Contributor

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
Copy link
Author

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
Copy link
Member

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
Copy link
Author

manuerwin commented Jan 12, 2024 via email

@ChristopherJohnston
Copy link
Contributor

ChristopherJohnston commented Jan 12, 2024

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:

  • On group creation, set a passphrase that would be shared with other members of the group. This could be placed in local storage similarly to the default participant.

  • The passphrase and groupID are used by the createKey method in libp2p/keychain to create a private key, which is stored in the browser's IndexedDB:

getKeychain(passphrase).createKey(groupId, "rsa", 2048)
  • When creating the group link, ask for the passphrase again then use the exportKey method in libp2p/keychain to export the private key from IndexedDB and and add it to the group link's querystring.
getKeychain(passphrase).exportKey(groupId, passphrase)
  • When loading a group from the link, ask for the passphrase which is used with the private key in the querystring to pass into the importKey method of libp2p/keychain.
getKeychain(passcode).importKey(groupId, privateKey, passphrase)
  • This would create and store the key in IndexedDB. Good thing here is even with the group link, you still need the passphrase to be able to see the data.
  • When an expense is created, use the encrypt method in libp2p/cms to encrypt the individual values before sending to the server
const cms = new CMS(getKeychain(passphrase));
const encryptedValue = cms.encrypt(groupId, uint8ArrayFromString(value, 'ascii'))
  • When an expense is received, use the decrypt method in libp2p/cms to decrypt the individual values before using them
const cms = new CMS(getKeychain(passphrase));
const value = cms.decrypt(encryptedValue)

Potential problems:

  • If the private key and passphrase are lost, all data is lost
  • there's limited places where code is executed on the client side
  • The encryption and decryption is not particularly performant

@ChristopherJohnston
Copy link
Contributor

POC of the above here: #41

@scastiel
Copy link
Member

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
Copy link
Author

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

@Pt4r
Copy link

Pt4r commented Jul 17, 2024

Are there any news regarding this issue?

@manuerwin
Copy link
Author

manuerwin commented Jul 25, 2024 via email

@david-alm
Copy link

david-alm commented Jul 29, 2024

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
Copy link
Author

@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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants