0

I'm storing a list of email addresses and other not personally identifiable information for a mailing list in a database. Since it's for a project related to cryptocurrency, I believe that the information could be a target for future attack. Specific scenarios I am aiming to protect against:

  • Someone in the shared hosting environment or a server administrator (for example, rogue employee) decides to copy the email addresses to sell on the dark web, where it could be used for phishing.
  • A part of the site or service is vulnerable to some kind of exploit that inadvertently gives access to the database. (Despite my best practices and careful development I left one single page open to SQL injection, for example.) This gets exploited by an attacker to get emails and phish the site members.
  • The server becomes vulnerable due to an exploit elsewhere on another shared hosting account, which is then able to gain elevated privileges and access the database using high level server access.
  • A backup service is used in the future, and this stores data insecurely or is not trustworthy, thus resulting in the copy of the backup being accessed and members getting phished.
  • At some point, the wrong individual is trusted to develop a feature of the site - either given access due to high trust or providing code with a backdoor that isn't noticed in careful inspection of provided code.

The above could be extremely damaging to the project. I'd like to ensure that the plain text email addresses can't be accessed in those or similar scenarios, should all other protection mechanisms and best practices fail.

The email addresses will only be used for a periodic newsletter, so there is no need to have persistent access to the information. The idea I have is to encrypt the email addresses using a secure private key that's stored offline and only accessible to me. I would use the key to decrypt the email addresses only when sending the newsletters, and otherwise email addresses would remain encrypted.

I had some basic questions:

  1. Which encryption algorithms make sense? I'd like something that's easy to work with in PHP, been around for a while, and that there aren't any known exploits.

  2. One issue is that new email address would have to be encrypted when a person signs up. I could leave them in plaintext for a period of time, but my concern is that's going to be a pain to keep doing and I might not get around to encrypting them quickly. Is there such a thing as an algorithm where one key can be used to encrypt the information, which is different from the decryption key? ie The decryption key works to decrypt any secret, but the encryption key is one-way only. I was curious if this is possible.

Bonus points if someone can provide some simple PHP examples that I could play with. That would be really awesome!

Note: I have checked out some other pages:

Mysql - two-way encryption of sensitive data (email addresses) outside of Apache, PHP and MySQL seems like quite a different use case, since they need continual access which I do not

Encrypting email addresses in php wants hashing, and also continual access which I do not

Thanks so much for your help and insight!

azoundria
  • 743
  • 2
  • 5
  • 7
  • Seems like if someone has access to the code which encrypts the e-mail, then they also have access to your key. So maybe don't encrypt it all... just create something you can run to export/delete the e-mails. Then encrypt/decrypt them at your end. Then you just need to secure your own credentials, and you can hash those. – pcalkins Oct 13 '21 at 19:56
  • I plan to provide the key every time emails need to be encrypted or decrypted and not store it anywhere. Someone can get access to the encryption or decryption code (a public algorithm), but they still wouldn't have the key. As I mentioned, that means that new sign-ups would sit without encryption until I got around to encrypting them, which is why I was hoping there might be a protocol with some sort of "encrypt-only" key, which wouldn't help in any way to decrypt, in addition to the master key that could decrypt too. – azoundria Oct 13 '21 at 22:38
  • the key is fundamental to the encryption so I'm not sure that the decryption key can ever be different...? access to and from could be secured differently... – pcalkins Oct 13 '21 at 23:13
  • Okay it may not be possible yet (or ever). I was thinking of a scheme where with one key it acts like a hashing function, but with another, it acts like a two-way encryption. If you've not heard of anything like that, then I will have to settle for what exists. – azoundria Oct 13 '21 at 23:19

1 Answers1

1

The difficulty here is that the web application must have access to the key in order to send emails. But if the application is breached, the attacker could obtain that key and decrypt the emails.

So total security is probably not possible, but you can make attacks more difficult. Indeed, storing encrypted data in the database could be useful since it prevents the emails from being leaked through a mere SQL injection – a more sophisticated attack involving remote code execution would likely be needed (depending on how you store the key).

A stronger security barrier would be to separate the email handling from the web backend. The web-facing code should never have access to the encryption keys, and should not be responsible for sending emails. The emails should be stored in a separate database. Instead, you could have a different server or service for email handling. This service can be much simpler than a web backend, and can therefore be easier to secure. In particular, you can configure highly restrictive firewall rules that prevent unexpected accesses. You can use encrypted communication between the mailing service and the web backend. You can assume that the web backend is already compromised, and set up alerts in case it shows unusual access patterns. You can expose endpoints only for expected tasks, in particular “subscribe address”, “unsubscribe address”, and “send email”, but not “decrypt and list all addresses”.

The most basic implementation of this would be to run your web backend and email handling service in separate Docker containers on the same host. This makes it more difficult to accidentally expose a port to the internet, and limits the “blast radius” of some (but not all) exploits.

Of course, nothing is foolproof. Typical weak points in such setups is misconfiguration, keeping the server software patched, and securing backups appropriately while still retaining the ability to restore them. There is also supply-chain risk: the mailing service through which you send mass emails could be hacked themselves (though they are probably able to have a much stronger security stance than you).

amon
  • 1,276
  • 8
  • 9
  • The idea I have would be to provide the key, or decrypt with it, each time I need to send out the newsletter. Otherwise, all key copies would be offline. Encryption of new emails would have to be done periodically, unless (as I hope) there is an algorithm with a separate "encrypt-only" key. Managing email addressess offline, deleting them from the database and populating them again prior to sending a newsletter, is certainly an option. It just seems like an awkward one, when compared with managing a static key. – azoundria Oct 13 '21 at 23:32
  • 1
    @azoundria Limiting the time during which the system is vulnerable is a good idea, though it might not offer protection once the system is already compromised. Keeping security barriers between different parts of your system tends to be a stronger protection. Note that you can use public-key encryption so that the public key can be used as an encrypt-only key, whereas you'd need the private key to decrypt data and send out the emails. – amon Oct 14 '21 at 14:50
  • Thanks amon! I tried out this example here: https://stackoverflow.com/questions/4629537/how-to-encrypt-data-in-php-using-public-private-keys - However, the end result encrypted text is 512 bytes in length. I tried to change the algorithm from SHA512 to SHA256, however the encrypted result was still the same length. Is there a way to have the result be slightly smaller (maybe 64 bytes or 128 bytes)? – azoundria Oct 14 '21 at 18:24
  • If the system is compromised, then I most likely know about it. There are ways I can add extra checks like comparing hashes of key files to protect against some attacks. If the system gets compromised without me realizing despite those checks, and steals the email addresses during the brief period where they exist in RAM to send the mail, this is a much more sophisticated type of attack than I have considered. I don't see how you would implement anything foolproof against that. There also is a risk I may make things too complicated and end up without implementing anything or losing data. – azoundria Oct 14 '21 at 18:31
  • @azoundria I'm confused – SHA are hash functions, and cannot be used for encryption. Even then, SHA512 produces 512 *bits* (= 64 bytes), though they are sometimes displayed as base64 or hex ASCII. The smallest you can get with asymmetric encryption is probably 256 bits for an ephemeral public Curve25519 key + one or more 128-bit AES cyphertext blocks, but it would require you to perform low-level crypto operations yourself to implement an [ECIES scheme](https://github.com/insanum/ecies) (risky!). Good news is you don't need the space for IV & auth tag here. – amon Oct 14 '21 at 19:06
  • And no, you won't find out if you were compromised. For example, there might be non-persistent attacks that don't modify the file system at all. You can make persistent attacks more difficult e.g. by using a read-only filesystem. And you could make attacks in general more difficult by reducing your attack surface, and by segregating email handling to a different server. Then, you can probably do without row-level encryption. But yes, overcomplication is very dangerous. The KISS principle also applies to security. – amon Oct 14 '21 at 19:08
  • Yes I am confused as well. However, the example at that link clearly has "digest_alg" => "sha512". 128 bit blocks (that's 16 bytes) is definitely smaller than needed, so somewhere in the middle would be fine. Right now the encrypted result is 4096 bits (512 bytes) of data. But I do want something simple I can implement in PHP and maybe even understand. It's true that I may not always find out if I'm compromised, but I believe it's more likely I would than wouldn't. I don't have an extra server at this stage and I believe most websites wouldn't in general. – azoundria Oct 14 '21 at 19:35
  • @azoundria Encrypted text is usually provided by a message authentication code which is derived via a hash function, but that might not be needed here since there is no transmission of the cyphertext. Your mention of 4096 bits suggests that you're using RSA, which is flexible but inefficient. Elliptic-curve cryptography can use much smaller key sizes at equivalent security levels, but is more difficult to use for encryption (the ECIES scheme I linked uses public/private keys to derive a session key to be used for *symmetric* AES encryption). – amon Oct 15 '21 at 13:50