0

We are building a service that manages multiple user accounts. For that reason, we need to store the credentials to a user's accounts using a symmetrical algorithm so they can be retrieved for the server to pull updates for the accounts. We however do not want to keep the keys in the database therefore here is the scheme we came up with:

  • User logs into our system using their password over a secure SSL connection. Each user account has a unique salt attached to their account and it is used to hash their password and verify against the database. Hashing function used for this process is bcrypt.

  • If user login credentials are confirmed, a symmetrical AES-128 key is generated from their unencrypted password and their unique salt and attached to their session to decrypt the credentials of the third party accounts stored in the database.

I am no security expert therefore I will leave it to you guys to tear our scheme apart. Let the carnage begin.

Anders
  • 64,406
  • 24
  • 178
  • 215
ReX357
  • 109
  • 1
  • Take a quick tout of the question in the "Related" box along the right side of this page. You might find some options in the questions others have asked. – schroeder May 09 '16 at 17:55
  • 1
    What happens when a user changes their password? suddenly their 3rd party account is now un-usable. Bad idea? – Robert Mennell May 09 '16 at 18:40
  • @RobertMennell That is a downfall we're willing to live with to keep our users' information confidential in case of an external or an internal breach. If a user changes their password, they will have to reenter the passwords to all their third party accounts so they can be reencrypted with new key. It would be worked into the password changing process as the first thing you would do when you log back in. – ReX357 May 09 '16 at 18:55
  • so they get decrypted, then encrypted with the new key if I'm understanding this right? – Robert Mennell May 09 '16 at 18:58
  • @RobertMennel In the case that the user changes their account password, they will have to reenter their passwords for all their managed accounts. The old ones will be worthless since there will be no way to decrypt them. Once you're logged in after changing your password, the server will attempt to pull updates and when the login credentials for an account do not work, you will simply be prompted to fix the credentials. Once you fixed the credentials, the reentered credentials will be encrypted with the new key generated from your new password. Think email client when you change your password. – ReX357 May 09 '16 at 19:40
  • That process doesn't make a whole lot of sense. In reality someone has stolen an encrypted version of those exact same passwords, and those passwords themselves haven't changed. This sounds like security theater. The passwords for those third parties haven't been changed, so they have still be stolen(albeit in an encrypted format), and can still eventually be brute forced. – Robert Mennell May 09 '16 at 19:43
  • @RobertMennell I understand what you're saying but with that way of thinking, symmetrical encryption would be useless and that's not the case. All symmetrical encryption can be brute forced at some point. The idea is to make it awfully inconvenient and resource intensive. Let's say we have a disgruntled employee one day that decides to peak at the database to get plain text passwords from corporate accounts and start messing with them? That could have been avoided or made much harder with our scheme. It would also be very inconvenient for a hacker to brute force the whole database. – ReX357 May 09 '16 at 19:50
  • However if they brute force a single person, all they would need is the old password for your site. If they have that, then they have the 3rd party passwords and don't even need to go back to your database again. You need to find a way to try and force the user to UPDATE those 3rd party passwords. That way you're not encrypting OLD data is what I'm saying. – Robert Mennell May 09 '16 at 19:55
  • @RobertMennell I think that's where we draw the line between convenience and security for our application. You can't force anyone to change their 3rd party credentials since we will have nothing to verify against because the old information will be stored in an undecipherable format. The only other thing we can do to increase security for that problem is to log the user's ip when they log in and require two factor authentication when they connect from an unrecognized device. – ReX357 May 09 '16 at 20:01
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/39520/discussion-between-robert-mennell-and-rex357). – Robert Mennell May 09 '16 at 20:14

1 Answers1

1

You don't really describe this step very well:

If user login credentials are confirmed, a symmetrical AES-128 key is generated from their unencrypted password and their unique salt and attached to their session to decrypt the credentials of the third party accounts stored in the database.

Exactly how is that 128-bit key created? That should be a very slow process, otherwise you are providing a side-channel to brute-force user passwords. The way it would work is an attacker gets their hands on your database. Rather than try to brute force the bcrypt hashes, which would be very time consuming, they get to work on the encrypted passwords. They simply brute-force the user password to get keys that appear to work (ie: the decrypted text looks like passwords, whatever that means). This will be a fast attack because encryption is fast.

You need to make the 128-bit key creation a slow process. The best way to do this is probably to use bcrypt. But, as you have probably figured out, you don't want to use the bcrypt value that you've stored in the DB for authentication as that would not be a safe way to store encryption keys. I suggest that you keep a second salt for each user. You then use that salt, plus their clear-text password, in a call to bcrypt to generate the 128-bit encryption key.

When a user changes passwords, you should either wipe or reencrypt all of the encrypted passwords. This is needed because users frequently change passwords when they are concerned that their password has been compromised. So you better wipe the data that was encrypted with the compromised password. Though this, of course, won't help if the attacker already got a copy of the database before the password change.

Another security measure you could add is to pepper (search for "pepper" in this answer) the encryption key. The pepper should be a truly random bit string (no, your cat's name doesn't qualify) that you store someplace else. That is, it is not in the database. Use of a pepper will prevent an attacker who gets both a copy of your database and a user's password, from decrypting the user's data as they will still need the pepper. Remember that a pepper is only as helpful as it is secure. Putting it in your source repo is probably a bad idea.

Neil Smithline
  • 14,621
  • 4
  • 38
  • 55
  • To generate the key we simply hash the plain password with the user's salt (Same one used to bcrypt) using SHA-256. The resulting hash becomes the key to encipher/decipher text. As for the concern about the database compromise, talking with Robert in the comments above, I came to the conclusion that we would also hash the third party account passwords and in case of a breach, we would force users to change their 3rd party account passwords by comparing the hashes of the resubmitted credentials to the ones in the database. – ReX357 May 10 '16 at 03:26
  • I like the solution you came up with though, to hash again with bcrypt using a different salt. – ReX357 May 10 '16 at 03:28
  • @ReX357 - you must rehash to slow down brute force searches for the password. – Neil Smithline May 10 '16 at 04:01
  • @ReX357 - I read your conversation with Robert. While I think that's a good idea, it is not perfect. Another thing that you can do in the face of a breach is to destroy your pepper. That will instantly invalidate all of the passwords you have stored. So, even if an attacker has the database and a user password, they'll be no way to get the pepper as you've changed it (and done your best to remove backups and such). – Neil Smithline May 10 '16 at 04:03
  • @ReX357 - In the end, storing recoverable passwords, however it is done, is risky. Systems like OAuth were created to prevent the need for password storage. But, if the services you're integrating with don't support OAuth, there's nothing that you can do about it. – Neil Smithline May 10 '16 at 04:05
  • I agree but it's a necessary evil in our case and the third party accounts are not all available under OAuth. There is always risk involved but we will not be risky about things. With the solutions we came up with in this thread as well as 2 factor authentication when users log in from unrecognized devices, I believe we will be providing more than good enough security measures to our users. – ReX357 May 10 '16 at 04:26
  • I think the phrase that is missing from your answer is [key stretching](https://en.wikipedia.org/wiki/Key_stretching). – SilverlightFox May 11 '16 at 08:37