Hi there everyone!

I plan on implementing an encryption method for my user auth's storage of email addresses. I've been doing a bit of reading and most up-to-date tutorials and posts suggest openssl_en/decrypt to do this. It looks like this function will do what I need, but I have a question about the implementation:

https://naveensnayak.wordpress.com/2013/03/12/simple-php-encrypt-and-decrypt/

Should I be using a key common to all the encryptions and then storing an iv separately associated with each email address? To clarify, the same key for each encryption and an IV for each one, stored in alongside the email address?

Thanks for your time!

    schwim wrote:

    Should I be using a key common to all the encryptions and then storing an iv separately associated with each email address?

    Why do you want to encrypt these email addresses, where will the encrypted email addresses be stored, and where will the secret key(s) be stored?

    schwim wrote:

    To clarify, the same key for each encryption and an IV for each one, stored in alongside the email address?

    Storing the IVs with the encrypted email addresses is fine. Of course, you cannot store the key(s) with the encrypted email addresses.

      Hi there Laserlight, and thanks very much for your help.

      My reasoning behind encrypting the email addresses of the userbase is the same reason we encrypt the passwords. In the event of a db compromise, it's a bit of info that people really don't like being exposed.

      I was planning to store the key in a flatfile outside of the public directory. I'm not sure that this the best way, but I'm finding very little info about these details on the web.

        We encrypt all user data for HIPAA compliance. We store the IV per user in the database, along with the encrypted data and the key in our server's environment variables. If someone gets the database they can do nothing (well I guess brute force might be an option if they are patient enough to wait the length of the entire universe!). No url will access the key, but if they gain SSH access all is lost and I think ssh security is by far the most important thing.

          schwim wrote:

          My reasoning behind encrypting the email addresses of the userbase is the same reason we encrypt the passwords. In the event of a db compromise, it's a bit of info that people really don't like being exposed.

          There is a difference though: passwords do not need to be encrypted such that they can be decrypted; they can be hashed. As such, even if an attacker had superuser access to all the servers involved, he/she would not be able to obtain the original password if it is well chosen and if the hash algorithm is sufficiently good.

          You cannot achieve this margin of security when encrypting an email address unless you do not store the secret key, but rather require the user to provide it via a secure channel (e.g., SSL/TLS) when needed.

          schwim wrote:

          I was planning to store the key in a flatfile outside of the public directory. I'm not sure that this the best way

          Since you are only concerned about a database compromise, this will satisfy your requirements as an attacker with access to the database will not have the secret key. Of course, an attacker with access to the code will have both the secret key and the credentials to access the database (which you have to store with the code otherwise the code cannot access the database), so this does not help in that regard.

          Incidentally, if you are using the email addresses for authentication, then be careful to normalise them since you cannot do a case-insensitive search (i.e., you would be encrypting the email address provided at login, then searching for a match of the encrypted email address to retrieve the hashed password and salt).

            Derokorian;11053295 wrote:

            We encrypt all user data for HIPAA compliance. We store the IV per user in the database, along with the encrypted data and the key in our server's environment variables. If someone gets the database they can do nothing (well I guess brute force might be an option if they are patient enough to wait the length of the entire universe!). No url will access the key, but if they gain SSH access all is lost and I think ssh security is by far the most important thing.

            Thanks a bunch, Derokorian. This is what I had in mind, storing it outside of the publicly accessible folder. Then I could pull it into the script on page generation.

              laserlight;11053297 wrote:

              Incidentally, if you are using the email addresses for authentication, then be careful to normalise them since you cannot do a case-insensitive search (i.e., you would be encrypting the email address provided at login, then searching for a match of the encrypted email address to retrieve the hashed password and salt).

              I would appreciate your input on this element as well. In my mind, I had considered using the following method to do the login check.

              First, when they register, email address gets strtolowered before being encrypted. In another cell in the row, I would store the number of characters in the address as well as first character, last char before @ and tld, so thisisme@here.com would get an entry of 17tecom. When someone attempted to log in, it would do the same to their email address, check the row for a match of the count and character string. If it found one, then it would do the authentication check.

              Even to me, this sounds convoluted, but I think I can make it work pretty well for a login check. I would very much appreciate everyones' thoughs on the matter.

                schwim wrote:

                First, when they register, email address gets strtolowered before being encrypted. In another cell in the row, I would store the number of characters in the address as well as first character, last char before @ and tld, so thisisme@here.com would get an entry of 17tecom. When someone attempted to log in, it would do the same to their email address, check the row for a match of the count and character string. If it found one, then it would do the authentication check.

                Even to me, this sounds convoluted, but I think I can make it work pretty well for a login check.

                Ah, my bad, I forgot about the IV: you would not be able to encrypt the email address supplied at login because you would not know which IV to use. However, your idea of storing those information in plaintext is bad: you would be revealing information that should remain secret, possibly allowing a known plaintext attack.

                Perhaps you could store a cryptographic hash of the plaintext email in the database, similiar to how passwords are stored, except that you use a site-wide salt (some people fancifully call it a "pepper") only that is stored in the same way as your secret key. This way, you can hash the email address supplied at login, then retrieve all the encrypted email addresses with hashes that correspond to the computed hash, and decrypt each one for comparison with the email address supplied. Typically, there will only be zero or one such result, but hash collisions are possible so you need to account for that (and so you would index that column, but not with a unique index).

                In theory, not using a salt specific to each email address reduces the security, but as the attacker would need the pepper to begin with (i.e., although we normally assume the attacker knows the algorithm, here we reasonably assume the attacker does not know the full algorithm since the pepper is effectively a critical part of the algorithm), this is not a concern since an attacker who has the pepper probably has the secret key, in which case the hash becomes unnecessary as the attacker can just decrypt the encrypted email addresses.

                  Depending on how you encrypt, you may be able to search using decryption in the query. For example, (actual query code):

                  WHERE CAST(AES_DECRYPT(fname_enc, :enc_key) AS CHAR) LIKE :fname
                     OR CAST(AES_DECRYPT(lname_enc, :enc_key) AS CHAR) LIKE :lname
                     OR CAST(AES_DECRYPT(email_enc, :enc_key) AS CHAR) LIKE :email
                  

                    Derokorian, would there be a similar query for encoding upon insertion?

                      I'm going to wildly guess that it would involve a function called AES_ENCRYPT.

                        Derokorian;11053307 wrote:

                        Depending on how you encrypt, you may be able to search using decryption in the query. For example, (actual query code):

                        WHERE CAST(AES_DECRYPT(fname_enc, :enc_key) AS CHAR) LIKE :fname
                           OR CAST(AES_DECRYPT(lname_enc, :enc_key) AS CHAR) LIKE :lname
                           OR CAST(AES_DECRYPT(email_enc, :enc_key) AS CHAR) LIKE :email
                        

                        I would point out that such an operation would require a COMPLETE TABLE SCAN to find a matching record -- i.e., this query would have to perform an operation -- a pretty intensive one I might add -- on every record in the database table.

                          sneakyimp;11053359 wrote:

                          I would point out that such an operation would require a COMPLETE TABLE SCAN to find a matching record -- i.e., this query would have to perform an operation -- a pretty intensive one I might add -- on every record in the database table.

                          Its true, and a big part of why we are changing our system to have plaintext indexing (first 2 characters) so people will only be able to search by the beginning of a name. I was more demonstrating that such an operation is possible. There is a lot of bad old code to get rid of, but some of it does things!

                            Write a Reply...