First off, my understanding of encrypting and hashing:
Encrypting - can be decrypted
Hashing - can NOT be unhashed
When building a web application, I should:
Encrypt the email address (will be used to login) with encryption key. It's nice to be able to decrypt email addresses for later use (e.g. emailing users)
Hash the password with a salt. No one should be able to see user's password, so hashing (since it is one-way) is good.
If the above 2 points are right, where should I store the encryption key and salt?
If I store it in the DB, the seems a bit pointless should the DB ever be compromised. The benefit, though, is that I can assign a unique encryption key and salt for each user.
Should I store the encryption key and salt in my application's configuration? If the DB is ever compromised, at least the encryption key and salt are not also compromised (hopefully). The problem with this is that it probably means that everyone shares the same encryption key and salt.
Suggestions on what to do?
If you encrypt the email at all, you need to do it with a common salt/key. Otherwise, how are you going to select a user by his email address from the db to check whether the hashed password is correct? You can't decrypt every email address every time.
Overall, I think there's very little to be gained from encrypting email addresses. Use MySQL database encryption if you want, but don't worry about this at the application level.
The salt for hashing the password should/needs to be unique and can be stored in the database, in fact it can be part of the hash itself. See http://www.openwall.com/phpass/ for a good implementation.
Your understanding seems correct to me.
Password: Only the hash of a password should be stored, together with a user specific salt. The salt can be stored plaintext, the reason for the salt is, that an attacker cannot use one single rainbowtable for all users (building a rainbowtable is expensive). It's recommended to use the hash_hmac() function.
EMail: I think it's a good idea to encrypt these adresses, but however you do it, if the attacker has control over the server, he will be able to recover these addresses. I would put a secret key in a separate directory, which is outside the web root (cannot be accessed directly from the web). Don't write it in a file that can be delivered without interpreting, the extension *.php is better than *.inc . If you have no access to such a directory, at least make one and protect it with .htaccess Deny from all.
If you need to find an email address in the DB you can additionally store a hash, this allows to search case insensitive (first turn to lowercase, then generate the hash).
The salt should be per-user, and can be indeed in the database; thus the point of a salt is that someone with a copy of your db can't work on cracking all the passwords at once, but each separately.
As for the encryption key, that's a much harder issue - definitely don't store it in the database; if your platform offers any kind of protected storage, you may want to use that. See e.g. this for useful answers: What's the best method to use / store encryption keys in MySQL
Related
Could someone please confirm the following for me:
Is the point of encrypting passwords when saving them into a database that if the database is hacked into then the hacker won't be able to know the actual passwords, unless s/he has the algorithm and salt etc to decrypt it, and therefore won't be able to compromise this or other accounts using the same password?
But my main query is: presumably the password is encrypted in, for example, the PHP script that saves the password into the database, and therefore the algorithm to decrypt the password is clear in that script. So is it correct that if the hacker hacked into the server or content management system for the website s/he would be able to access that script and decrypt the passwords?
So essentially the encryption is only as relevant as your login information to your online CMS or server is strong?
Thanks in advance!
Your passwords shouldn't be encrypted in the database.
What is commonly done is taking a hash of the passwords, and storing that in the database. A hash is a one-way function. It isn't possible to reverse it and get a result. To check to see if a password is correct, the test password (what the user enters) is re-hashed with the salt to see if it matches the has from before.
This way, should someone obtain a copy of the database, they only know the hashes, which take an incredibly long time to find a collision (match) for. Adding a unique salt for each password ensures that users with the same passwords have different hashes, meaning the work to find hash collisions has to happen for each password (very slow).
You're missing the point. You do not store encrypted passwords in a database, you store password hashes in the database.
You do not want to decrypt the password, you want to compare the stored hash with a calculated hash!
Passwords aren't actually encrypted. They're actually hashed via a one-way hashing algorithm. This means that "theoretically", an attacker shouldn't be able to reverse the hash. Problem is: A lot of beginner web developers will use hashing algorithms that are fast. This means that the usage of lookup tables becomes an issue, where a script can be used to hash a whole bunch of dictionary words and then compare them against the hashed password from the DB.
So,
I have a mysql table which stores passwords, and these passwords can not be hashed, cause I need to recover it to plain text later.
I have a javascript, which via ajax/php takes these passwords from a mysql database and sends it to another server that will use it to authenticate, that's why i need to have them in plain text when I send.
I know there's base64 and other encryptation alghorythms, but that's unsafe.
The best solution I found is OpenSSL, but I'm not sure if I'm on the right path. Am I?
OpenSSL is a good place to start looking. It supports a very large number of secure encryption algorithms that you can use to encrypt the plain text passwords. AES-256 or Twofish are good algorithms to start looking at. 3DES is also considered sufficient to today's standards.
For good security, you will need to encrypt each user's password with a different key; that is each user has a unique encryption key to them and you do not use 1 key for all passwords. This could be a hash of the user's password that they use on your site, but often user passwords aren't strong, and if they forget the password to your site/service, then they also lose their encryption key.
For the greatest security, you shouldn't store the encryption keys anywhere. When the user logs in with their password, you can generate the encryption key in memory based on their password. Ideally it would not just be a hash of their password, but their password applied through some sort of transformation algorithm.
If that isn't an option, then you should store the encryption keys on a different physical server than the one that stores the encrypted user passwords. The server that stores the encryption keys should have a number of security and access control features in place with very controlled database access so pretty much only your application can access the keys.
And on top of that, you must disclose in your privacy policy that you may store encrypted forms of their passwords for use with the 3rd party service.
Hope that helps. OWASP may have some other helpful information related to what you are going to do.
thanks for all the answers, I'm going to use an php encryptation method described here: https://stackoverflow.com/a/6639179/1415262
and try some openssl.
For all of the other answers, I have a few problems with them and short time to explain why.
PS.: I can't up vote yet, but special thanks to #drew010 and #fabio :)
I would HIGHLY recommend that you don't store passwords in plaintext and maybe generate some kind of one-time usage key that is passed from one server to another.
So server one has a key linked to a specific user that is unique, this key is also on server two and that is the key that's passed.
I have read about using MySQL AES_ENCRYPT/AES_DECRYPT (two-way encryption) is less secure than using PHP - hash() (one-way encryption).
http://bytes.com/topic/php/answers/831748-how-use-aes_encrypt-aes_decrypt
Is it true that it is more secure that 'Rather than send the User his password, simply send him a link that he can click on to reset his password, instead.'?
And on top of that, if I am using MySQL AES_ENCRYPT/AES_DECRYPT (which I quite keen on...), how do I define the key which can be accepted by MySQL? for instance, is the length of the key important? or can I simple use '123123#123123' as my key?
thanks!
There is a fundamental difference between the two concepts, hashing and encryption:
Encryption can be reversed, hashing can't (at least that's the idea).
If a malicious user gains access to the passwords in a database and knows the key you used to encrypt them, they will be able to recover said passwords. If they are hashed, they won't be able to do that.
That's why passwords should be always be hashed (and salted), never encrypted.
for instance, is the length of the key important? or can I simple use '123123#123123' as my key?
AFAIK MySQL's AES_ENCRYPT can take keys of arbitrary length; but obviously shorter keys will make it easier for an attacker to bruteforce it (ie: try all possible combinations)
Two way encryption is inherently less secure because the real data is stored somewhere. That is, you have a password "hello." Then you hash it, you get 5d41402abc4b2a76b9719d911017c592. This is meaningless to a normal person and they will not know how to decrypt it without knowing the correct encryption algorithm. They cannot use this either because only the original password is used. You check a password by hashing it and comparing it to the hash (also stored). 5d41402abc4b2a76b9719d911017c592 hashed is 69a329523ce1ec88bf63061863d9cb14, so they don't match. Even if a user knows the hashed password, he can't get anything out of it.
So you can store the encrypted data, but if you decrypt it when you are pulling it out then anyone can use it.
The security of sending a user a link compared to giving them the password is a different issue. If you email the password, it is printed out in plain text for all to see (and use). Giving them a link to allow them to input a new password means no one will see it which is a bit more secure, but if someone committing fraud has access to that link anyway it is going to cause problems.
About AES, I can't find out too much on it at a glance, but it looks like it doesn't matter what you encrypt. So if you use AES_DECRYPT(AES_ENCRYPT('x', 'b'), 'b'); it will return 'x'. You have to keep track of the key.
If you are storing passwords on your server with symmetric encryption, you have to decode the stored password to test it against a user-submitted password. That means the key also has to be stored on the server. Which means anyone who compromises your webapp can retrieve and decrypt every user's password. (And use them to compromise other accounts where the user has used the same password.)
Hashing a password means that you can't leak the password to an attacker because you don't even know what it is yourself. You can still check whether a submitted password is the same as the original password by hashing it using the same algorithm and salt, so you can still tell whether a submitted password is right or wrong, without having to know what the password is.
Using hashed passwords does mean you can't tell the user what their password was in a ‘recover password’ option. But you don't really want to do that anyway, especially over an insecure mechanism like e-mail. One-time, time-limited reset-password links serve the same purpose with less potential damage.
For passwords, one-way hashes are almost always the way to go. One-way hashes mean that there is far less likelihood that anyone but the user would be able to know their password.
If you choose the one-way route, then you'll need to set up a password reset method. If this is done correctly, it should be fairly secure for most purposes. To gain better security, you can add things like security questions (e.g., "What is your favorite color?") that the user would have to answer before receiving a password reset link in an email.
As for keys for AES_ENCRYPT/DECRYPT-- MySQL will accept variable lengths for the key parameter to the functions, but it will use a 128-bit key regardless, so it's to your advantage to pass at least 128 bits' worth.
One-way encryption means you can only encrypt. (For example, you encrypt a password and store the result. Whenever a user authenticates, you encrypt what the user enters and compare. There is no need for a decrypt function in such a scenario.)
Two-way encryption means, there is both an encrypt and decrypt function available. In PHP, that is accomplished through the mcrypt_encrypt() and mcrypt_decrypt() functions.
An update! mcrypt is deprecated in PHP 7.1 and removed in 7.2. See OpenSSL or Sodium instead for encrypt and decrypt functions.
I have a list of students that are being added via a form inside the admin area. I'm trying to come up with a password generating solution for each addition that will be both secure and viewable from the admin panel (like below).
I need it to be viewable so that the admin will be able to print out the passwords and hand them out to the parents. But I also want them to be secure in case there's a database breach. Any ideas?
If you want the passwords to be viewable, they can never be really secure in case of a breach.
You may be interested in checking out the following Stack Overflow posts for further reading:
Difference between Hashing a Password and Encrypting it
How should I ethically approach user password storage for later plaintext retrieval?
Store passwords in 2 forms:
1) MF5/SHA1 hash for secure validation
2) AES encripted with master password. I.e. in order to view passwords you enter master password and bingo. In case of theft attacker would not get passwords that easy (but can bruteforce).
This is one of the few times I would say the software shouldn't be adjusted to the user(s). You're talking a major security risk here.
I would advice making some kind report generator to print passwords that creates (generates / salts and hashes and saves) them on the fly for printing. With this, you could generate the letters to be send as well. Makes the process mostly automated and a person would only have to send them to the printer (if that's even necessary).
Good luck.
You should not do this.
Generate a one-time password that can be used (and could also be stored in clear text) to set a new password via web.
As soon as the passwords are printed, they can be easily accessed by others, so it does not matter at all if you store them encrypted or not.
You can have one XOR the other.
If the passwords are to be secure, you mustn't store them in the database (store some_hash(per_user_salt + password) and compare that on login (as #Daniel Vassallo says)
If the passwords are to be viewable, then you must provide some way to get to the passwords - and if there is one, it can be abused (e.g. passwords stolen). If you decide that you absolutely, positively need to do this, encrypt the passwords in your application before storing them to the database. This won't shield you from all threats, but at least the passwords won't be readable if someone "only" steals your database.
Others have had the right idea, but were missing an essential step. You should use asymmetric encryption and store a public-key encrypted form of the password + salt.
To verify a password, take the proffered password, combine it with the salt, use the public key to encrypt the combination, and compare it with the stored value.
To retrieve the password, use the private key (kept secure, i.e. on another isolated machine) to decrypt the password + salt and throw away the salt.
Cons: asymmetric encryption can be expensive, computationally, but passwords tend to be short.
You could combine this with other ideas above (i.e. also store a salted hash), and you should pad the password so that the length of the encrypted text doesn't leak the password length.
I'm making an application in PHP and there is a requirement that it must be possible to decrypt the passwords in order to avoid problems in the future with switching user database to different system. Consider that it's not possible to modify this future system's password method and I need plain text passwords in order to have the passwords generated.
The plan is to encrypt the user's password with a public key that is stored on the server. Authentication is done by encrypting the input and comparing the results. There is NO decryption done. The private key capable of the decryption is stored off-site for later usage.
What encryption/decryption algorithm would you suggest? Are the encrypted passwords still as safe as hashing (MD5/SHA1) when you consider the private key is not available to the attacker?
I'll rephrase Jammer's approach -
Generate a public/private key pair. Hard-code the public key on your webserver. Store the private key in a physical bank locker, outside the reach of webserver/database/any developer.
When user registers, encrypt password + salt using public key. This step is identical to using a hash algorithm. Store the encrypted password + salt in the database.
When you want to verify the password, encrypt it again, and compare it to the value stored in the database.
If an attacker gets the database, he can't decrypt the passwords because he doesn't have the private key. He cannot get the private key because it is in a bank vault outside his reach. Two identical passwords will still be stored differently in the database because of the salt.
I don't recommend using the above approach because at any point of time in the future someone could abuse the private key and get access to all passwords.
But if you guarantee that the private key will always remain private, then I don't see a technical flaw.
I could be wrong, of course.
Don't decrypt the password. If you need to change the password system in the future, add a field called storage_type (or whatever).
Then, when you need to change the passwords, you will check if it's an old password. If it is, next time they login, you can change the password encoding. Otherwise, login with the new system.
Being able to decrypt the passwords is a bad idea (and there's probably not any way of doing it that would be much better than storing them unencrypted). It sounds like your main problem is being able to use the passwords if you change your storage method. Just do what Linux does, store how you're hashing the password with the password. So for example $1$salt$hash is MD5. That way, if you decide to change how passwords are stored, you can still check against the old passwords (and if someone logs in correctly, you can update their password with the new hash).
The only problem I see is that most public-private key encryption code out there will encrypt a symmetric key using the public key, and rely on the private key decrypting that, then use the symmetric key to encrypt the message.
You want to use the public key to directly encrypt the password+salt.
So attacks against your system boil down to:
Attacks against general public/private key encryption
Attacks against your private key store.
For most applications it is more than sufficient to store SHA-1 hashes of passwords.
Yes, there are known collisions in most hashing algorithms, but that doesn't imply an actual attack vector. Especially when you're salting the hashes.
For your salt: Store it in a configuration file that is not accessible from the outside but can be read by your PHP installation.