I've been reading about 2FA and how it is used, and the thing that struck me most is that everyone seems to be storing the TOTP secret as plaintext in their database.
I understand you need the secret as plaintext in order to verify the OTP, so you can't hash it, but couldn't you at least encrypt it in a certain way, in case your database gets leaked? I feel like there are ways to make this a bit more secure but I'm wondering if there are downfalls to the ideas I have.
- Encrypt the TOTP secret using key stored on the server: This wouldn't be ideal since you'd be encrypting everything with the same key, but a database can be leaked without the attacker having full access to all files on the server. In that scenario, at least the TOTP secrets are still protected.
- Encrypt the TOTP secret using the user password: When the user logs in and the password hash check is valid, the same password that was sent could be used as a key to encrypt/decrypt the TOTP secret. When the user first sets up his 2FA, you require that they enter their password so we can use it to encrypt the TOTP secret. When they login with a correct password, you decrypt the TOTP secret using the password, and you validate the OTP. If your database is leaked, password is hashed and TOTP secret is encrypted, so the attacker has no information on any account unless they know the password.
- Combine both methods: Combine the password with a locally stored key, and use this as the encryption key for the TOTP secret. This way, if the database is leaked and the attacker knows the password, they still can't decrypt the TOTP secret unless they have access to the stored key
Are there any flaws in this, and if so, what are they? I feel like anything would be better than just storing the TOTP as plaintext.