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

Encrypting sensitive (payment) data #180

Open
The-Ludwig opened this issue Jan 22, 2023 · 5 comments
Open

Encrypting sensitive (payment) data #180

The-Ludwig opened this issue Jan 22, 2023 · 5 comments

Comments

@The-Ludwig
Copy link
Contributor

The-Ludwig commented Jan 22, 2023

For the SEPA lastschriftmandat milestone we somehow need to save the payment information of some of our members.
Since this is the first time we ever really have to store highly sensitive data (compared to membership status this is orders of magnitude more important), we should really sit down for a moment and think about security.

We definitely need to access the payment information, so we are of course not able to hash the information. But we could additionally asymmetrically encrypt the payment information in the database.
The decryption key could then be stored outside the server, e.g. handed privately to the managing board of PeP (3 times in total)
Pros:

  • Even server-admins can't access the data
  • In case of a security breach into our server, the attackers can't get any payment information
  • We only use the Lastschriftmandat once a year, so using a little script where we then decrypt the information for a once-a-year use is totally feasible

Cons:

  • members of the database can't look at or change (but they can update) their saved information. We would need to e.g. save the last three digits of the IBAN unencrypted to let them remember which details they used.
  • More complex application
  • Semi-false sense of security for us and the users: We need to access the information somehow after all. If e.g. also the private key is compromised, or gets lost, it's no use after all.
  • Heavy briefing of the board members: where to store the key, where not to store the key, what to do etc...
  • Loss of key means loss of all payment information
  • we will save the transactions of the bank account of pep unencrypted somewhere anyway, so in this sense it's also available unencrypted anyways, just not neatly provided in a database

Considering these points I think encryption is not really needed here after all. It's better to double and triple check our system to make it hard to compromise it...
Although now I am kind of eager to implement the encryption into the database!

Let me know what you think! (related PR: #179)

@The-Ludwig
Copy link
Contributor Author

Another comment: Saving it encrypted and just letting the flask/django app also know the private key to decrypt it on-the-fly is somewhat pointless. Although this would prevent data-leakage in case only the database is compromised. If the whole app is compromised, where fucked then.

@The-Ludwig The-Ludwig changed the title Encrypting sensible (payment) data Encrypting sensitive (payment) data Jan 22, 2023
@ccauet
Copy link
Member

ccauet commented Jan 22, 2023

I think your proposal is completely resonable. Do asymmetric encryption, store the encrypted data in the DB and do backups. Then if you need to handle it once a year, get the info from the DB, and do the rest offline. No need the App needs to take care of the SEPA logic, right?

In the old days™️ we used to keep everything on file in an office shelf, and then you were told to hand over a floppy disk to the Volksbank.... so why not keep the private key for decryption on a piece of paper in a drawer. I would argue someone going into a university office to physically get hold of a private key is not one of the 99% of most possible attack vectors we are talking about.

On contrary

It's better to double and triple check our system to make it hard to compromise it...

is far easier to miss.

Semi-false sense of security for us and the users: We need to access the information somehow after all. If e.g. also the private key is compromised, or gets lost, it's no use after all.

As a donor, I expect you to handle the information. My only concern is, that no third party can access it.. Loosing the information is a worst case, thats true. Still printing it out, might not be the worst idea.

So, my baseline is: make it a scenario where a physical attack is the only successful one, and I think we are fine.

just my 2c

@The-Ludwig
Copy link
Contributor Author

The-Ludwig commented Jan 22, 2023

@ccauet Thanks for your opinion! And after being nerdsniped for the day and reading into cryptography, I agree with your 2c. Great resource can be found here.

Database-level encryption seems to be not that easy to install and deploq for PostgreSQL (which we use in production). I think now that we want to store sensitive data on the server, we should enable disk-encryption on the server (which may be already activated, I am unsure), which protects the data on the "thief steals disk" level.

For the really sensitive data columns, I would encrypt them. I'd use nacl.public.SealedBox for the actual data encryption and generate the DEK there and save the private key encrypted by nacl.secret.SecretBox with an KEK generated by a password by nacl.utils.randombytes_deterministic.
Then it's only possible to restore the information if you have the password to generate the KEK, which will decrypt the DEK.

This is the ultra-secure path, where even the application itself can't decrypt the data, without the user provided password (rememberd by the pep-board). The less-save-but-common-practice would be to just use a symmetric key to save sensitive data and only provide the symmetric key at the start of the application and save it in RAM while the application is running (using .env files exposes the key to the filesystem, if it is compromised). This means if the application itself or it's memory is compromised, we still have IBAN leaks, but not if the server is compromised.

@lena-lin
Copy link
Contributor

Sorry for the slow reply, here are my (mostly non-technical) thoughts on the SEPA stuff:
As far as I understand our online banking UI, we are able to manage everything regarding the Lastschriften within the account. The IBANs are stored in our account and the Lastschrift can be scheduled to a frequency of once per year without adding the same IBANs again. It is possible to delete all IBANs manually, but by default, all data is stored anyway.
Since the access to this data is limited to the board (and probably the Volksbank itselt), we decided several years ago that this is not a security issue and we kept it that way. Having frequently used IBANs pre-filled in the UI makes the life of the Finanzreferent/in much easier :)

Now comes my actual point: Do we really need to store the bank account data of our members in the DB? After the online registration, we could send an email with the data to the Finanzrefernt who then has to fill in the data into our online banking account manually. This is not a very high-tech approach, but this would avoid any security issues we might have if we proceed with the double bookkeeping in the DB. The transfer to the online banking account UI has to be done anyway. It might be possible to upload lots of IBANs to the account by uploading a csv or something, but as far as I know our members, we will not have hundreds of IBANs to be added :) So the workload for adding IBANs and scheduling the Lastschriften will be limited.

@ccauet
Copy link
Member

ccauet commented Jan 24, 2023

In princicple I would second your approach @lena-lin

After the online registration, we could send an email with the data to the Finanzrefernt...

This would be the only transfer that may be unsecured. When I hand over sensible data to some entity via a TLS secured website, I would assume this as the minimal level of security that gets applied. So breaking out of this, and send data unencrypted over the Internetz seems inapproriate.

So while trying to keep it very basic, we could:

  1. Keep a public key on the server/inside the app
  2. Encrypt new banking details with this key
  3. Send the encrypted data to the Finanzreferentin by mail who owns the private key to decrypt
  4. Keep the last 4 digits of the IBAN unencrypted in the DB, so a user could check the provided information
  5. Keep the encrypted data there for some days as a fallback
  6. Clean up everything on the server...

What do you think? Simple enough?

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

No branches or pull requests

6 participants