I have questions regarding the best way to secure the authentication of users.
I have come across a web application that encrypts the user password in the back end. However this encryption is done with the same key to all passwords.
Also, this key is "hardcoded" in the back end.
They (the app developers) claim that this is perfectly secure. However I have my doubts. I believe that this method can cause two problems:
The reason to encrypt passwords is to avoid access to the passwords in the event of an unauthorized database access. However if you store the key in the same server chances are they will also be able to obtain the key.
The same password will yield the same encrypted value therefore it will be easier to attack the system.
My questions are the following:
Am I right about my claims? And if it is really that insecure, should I warn them about the possible threat?
What would be the pros and cons of using a hash + salt approach instead?
Thanks!
I'm not sure if you might mistakenly mixed up encryption and hashing together. If the user's password is be encrypted and not hashed then there is potential for an attacker to steal all the user password in the event of a data breach.
There are a number of factors that you seem to be looking over when it comes to authentication. Firstly, any hashing should be done in the back-end and never in the front-end. Hashing in the front-end still leaves you vulnerable to hash attacks.
Some developers adopt a double-hash approach in which they hash the password in the front-end and then re-hash it in the back-end. I believe this is unnecessary, the front-end password should be covered by the HTTPS layer (TLS), however that is subject to discussion.
First, let's clarify two key terms before explaining how to securely store and authenticate users.
Encryption
You specify that the user's passwords are being encrypted, rather than hashed. What encryption functions do is map an input (user's password) to an output (encrypted password) in a 1-to-1 fashion, meaning that this is reversible.
This means that if the hacker gains access to the encryption key (private key), they can reverse the entire process easily.
Hashing
Instead, the user's password should be hashed on the server-side. Why? Because you can get away with comparing two hashes to check whether they match without ever storing the plain-text representation of that value.
And once again, you may be asking, "Why"? Well because hashing functions are one-way, meaning that the plain-text value cannot be reversed (well, they are very hard to), I shall not be going into to much detail.
What should I do?
The user's passwords should never be stored as plain-text in any part of the web server. Instead, you should be storing the user's hash. When the user then tries to login, you receive their plain-text password securely over HTTPS/TLS, hash it and if both hashes match, authenticate the user.
So a database table might look like so:
+--------------------------------------+
| ID | Username | Password Hash |
+--------------------------------------+
| 1 | foo | $2a$04$/JicM |
| 2 | bar | $2a$04$cxZWT |
+--------------------------------------+
Note, the hashes are truncated BCrypt hashes with 4 rounds (AKA - Invalid)
Now let's take an example, between Alice and our server. Don't take the data too literally.
Alice sends a request to login with her credentials, which first passes through our secure transport layer:
{username: "foo", password: "bar"} -> TLS() -> ZwUlLviJjtCgc1B4DlFnK -> | Server |
Our server receives this, then uses it's certificate key to decrypt this:
ZwUlLviJjtCgc1B4DlFnK -> KEY() -> {username: "foo", password: "bar"} -> Web Application
Great! Our credentials have been passed securely, now what? Hash that password and compare against what we got in our database.
BCRYPT('bar') -> $2a$04$/JicM
if ($2a$04$/JicM == user.get_password_hash) {
authenticate();
}
else {
return status_code(401);
}
We have now been able to authenticate a user, storing an irreversible hash value and without ever storing the plain-text value. This should have answered your first and second question.
Yes, your analysis is correct, this is insecure.
It would certainly fail any formal audit, e.g. PCI-DSS. the developers/operators may argue that the asset these accounts provide access to is of little value and hence they have no need to provide such a level of protection, however they still have a duty of care to their customers - and the majority of people will use the same password for different sites/services.
It does provide a means for users to "recover" their passwords without the complexity of creating an expiring OTP - however mailing a plain text password further undermines security.
Indeed, even if an attacker only had access to the encrypted password data (particularly if it contained a known encrypted value / does not use initialization vectors) it may be possible to derive the encryption key.
Related
Ok, so here's the deal: (This is a hypothetical scenario)
Say for some reason I had a server (e.g. a legacy one) that can not use encryption in transmitting passwords over the net
I will still want to store my passwords in the database safely, e.g. as a dedicated password hash like bcrypt. That of course means I can't hash them on the client side and compare the two hashes server side.
Now here's the idea: If I stored a bcrypt hash of e.g. a SHA-256 hash of the cleartext password in the database, I could then compute the SHA-256 hash of the cleartext password over the net and still compare that to the bcrypt hash of the SHA-256 hash on the server side.
The big questions are:
Is this safe, or is it opening me up to some sort of attack I'm not thinking off right now?
Would this approach actually gain me anything, or would I just be chasing my own tail?
The problem here is, if you want to compare hashes as I described, you must by definition use a deterministic hashing function, which means there can not be a variable element like there is in a proper password hash like BCrypt. Every time you authenticated, the hash you send would have to be the same.
That means that approach will protect the user's clear text password to a degreee if the communication is observed, and it protects the user in case the password database is lifted, but it does not solve the problem of transit security.
An unencrypted connection means your communication can potentially be observed. Since you would be sending the same hash value over the net with every login, anyone who managed to intercept one communication would still be able to impersonate you, and gain access to the system in question.
I have a database and an API in NodeJS, I create users with web app, and each user can create/update/delete data.
To secure this a bit, I need to encrypt data of users. So what I want is creating a pair of SHA256 public private keys each time a user is created.
Actually what I do is storing thoses keys in database, by encrypting them with global SHA256 pair of key.
So, in a nutshell, I have a global pair of key to encrypt each specific pair of keys for each user.
The fact is that seems to be not really secure because finally each user have his own encryption/decryption method stored in the database.
For example I can have 2 tables :
User table :
id_user | firstname | lastname | encrypted_data
-----------------------------------------------
1 | John | Doe | QMwmuCMmI..
2 | Jane | Doe | QMwmuCMmI..
...
Keys table :
id_user | public | private
------------------------------
1 | MIICIjA.. | MIIJrT..
2 | MIICIjA.. | MIIJrT..
...
So the link from John Doe to his public and private keys in simple.
A problem is that I can't ask for user to create a pair of private/public key and send me only public, because all need to be automatic, user don't have to do anything.
Another problem is that the application should be usable on any device, so the private key can't be stored in client side.
You can't just store them on the server as the attacker is on the server. The malicious hacker can easily find the keys (as they are un-encrypted), download the entire database and decrypt everything.
So, your best options are storing the keys on a completely different machine and let that machine do the encryptions and decryptions. That way the attacker would first have to break into your machine, and then the extra step of into the encryption/decryption machine. Not impossible, but hopefully much harder.
Basically, nothing is un-hackable. Making something as difficult as it can be for the attacker is the way to go.
Also, even if you strictly have to store the data in a database that your main server has access to, do NOT store them in the same table (as shown in your example how you kept them in separate tables). Keep the indexes of all data added to the user database the same, so that index 1 of users database will result in the correct key for that index (exactly what you did).
Another option would be cryptographic hardware. I'm not familiar with this, but I do know that some companies sell hardware that perform all of your cryptographic needs.
Disclaimer: This answer exposes two solutions to secure user data, but exclude SHA256 public/private keys from the equation (for, I believe, something more secure). It might not be an acceptable solution.
The "easy" way
I believe it's how Termius does it. In this case you would use the password to secure both the account access and encrypt data.
You would end up with something like that in database:
id
email/username
password_hash (to secure the account access)
... as many fields as you want, values are encrypted using a symmetric algorithm (i.e. AES) using the unhashed password
Advantage:
(optional) end-to-end encryption
instead of doing the decryption server-side, send the encrypted data as-is to the client. It will be the client app responsibility to do the decryption before the visualization, and to encrypt everything before sending it back to the server.
Drawbacks:
potential data loss
if an user loose its password, he also looses access to all its data.
the encryption key is stored in the database
sure, it's hashed. If the hashing algorithm is strong enough, it could be enough security for quite some time. But for some 3 letters agencies, it might be reversable relatively fast 🤷‍♂️
Warnings:
don't use the password hash as the key, only the unhashed password. Otherwise, the encryption key is stored "as-is" in your database (the password_hash field).
always use a salt when you compute passwords hashes (might sound obvious to say this in 2021, but still, I prefer to say it)
Even more robust encryption
I believe this is the way ProtonMail encrypts its users' data.
When you create a Protonmail account, you need to provide two passwords: the first one is used for account login, the second one for data encryption.
Advantages:
you don't need to store the second password in your database at all (hashed or not).
If the data can't be decrypted, it means the second password (aka the encryption key) is incorrect, its as simple as that.
(optional) end-to-end encryption
same as described in "the easy way".
Drawback:
potential data loss
if an user looses its second password, he also looses access to all its data. If he looses its first password, then he just looses access to "authentication". The typical "I forgot my password" flow works here, without raising any issue related to the encrypted data.
Measuring the pros and cons
Both solutions have a main drawback: potential data loss.
If you go for the Protonmail-like solution, I would say this drawback is compensated by a major advantage: the user is the only actor of its data's security, you store no hints about the encryption key in your database. Therefore, if you use industry-standard encryption algorithms you aren't responsible at al. Hackers would have no solution other than brute-forcing the encryption key of every user, one by one.
Plus, if you educate your users about the consequences of loosing their password, you can also claim that you are 100% unable to access their data at the same time. This is the highest possible level of data privacy, and this is what the world needs IMHO (no matter what type of data we are talking about).
I have read the following article http://lifehacker.com/5919918/how-your-passwords-are-stored-on-the-internet-and-when-your-password-strength-doesnt-matter
There are a number of ways a site can store your password, and some are considerably more secure than others. Here's a quick rundown of the most popular methods, and what they mean for the security of your data.
Method One: Plain Text Passwords
How It Works: The simplest way a site can store your password is in plain text. That means somewhere on a their server, there exists a database with your username and password in it in a human-readable form (that is, if your password is testing123, it is stored in the database as testing123). When you enter your credentials on the site, it checks them against the database to see if they match. This is the worst possible method, in security terms, and most reputable web sites do not store passwords in plain text. If someone hacks this database, everyone's password is immediately compromised.
Does My Strong Password Matter? No way. No matter how long or strong your password may be, if it's stored in plain text and the site gets hacked, your password is easily accessible to anyone, no work required. It still matters in terms of hiding your passwords from, say, your friends, or others that could easily guess it, but it won't make any difference if the site gets hacked.
Method Two: Basic Password Encryption
How It Works: To add more protection to your password than plain text provides, most sites encrypt your password before they store it on their servers. Encryption, for those of you that don't know, uses a special key to turn your password into a random string of text. If a hacker were to get hold of this random string of text, they wouldn't be able to log into your account unless they also had the key, which they could then use to decrypt it.
The problem is, the key is often stored on the very same server that the passwords are, so if the servers get hacked, a hacker doesn't have to do much work to decrypt all the passwords, which means this method is still wildly insecure.
Does My Strong Password Matter? No. Since it's easy to decrypt the password database with a key, your strong password won't make a difference here either. Again: this is in terms of the site getting hacked; if you have a nosy friend or family member rooting through your stuff, a strong password can help keep them from guessing it.
Method Three: Hashed Passwords
How It Works: Hashed is similar to encryption in the sense that it turns your password into a long string of letters and numbers to keep it hidden. However, unlike encryption, hashing is a one way street: If you have the hash, you can't run the algorithm backwards to get the original password. This means a hacker would have to obtain the hashes and then try a number of different password combinations to see which ones worked.
However, there is a downside to this method. While a hacker can't decode a hash back to the original password, they can try many different passwords until one matches the hash they have. Computers can do this very fast, and with the help of something called rainbow tables—which is essentially a list of trillions of different hashes and their matching passwords—they can just look up the hash to see if it's already been discovered. Try typing e38ad214943daad1d64c102faec29de4afe9da3d into Google. You'll quickly find that it's the SHA-1 hash for "password1". For more information on how rainbow tables work, check out this article by coding guru Jeff Atwood on the subject.
Does My Strong Password Matter? In this case, yes. Rainbow tables are made up of passwords that have already been tested against hashes, which means the really weak ones will be cracked very quickly. Their biggest weakness, however, isn't complexity, but length. You're better off using a very long password (like XKCD's famous "correct horse battery staple") rather than a short, complex one (like kj$fsDl#).
Method Four: Hashed Passwords with a Dash of Salt
How It Works: Salting a hash means adding a random string of characters—called a "salt"—to the beginning or end of your password before hashing it. It uses a different salt for each password, and even if the salts are stored on the same servers, it will make it very hard to find those salted hashes in the rainbow tables, since each one is long, complex, and unique. LinkedIn is famous for not using salted hashes, which brought them under a lot of scrutiny after their recent hack—had they used salts, their users would have been safer.
By reading the above article i have the following questions in mind
1.Even if i do not have the password,i still can intercept the message digest......i dont even need the password ...i will simply launch reply attack(ie. send message digest itself for authentication after intercepting it!!)
the solution to above problem can be solved by following way
a.server genrates a random string(usually known as challenge) to the user and asks him to encrypt it with his password .....
b.user enters his password,message digest of the password is created ,random string is encrypted by this message digest
c.this encrypted string is sent to server.
d.server also encrypts random string with message digest of user,checks it with encrypted string recieved from user,if both match,he is valid user..!
2.My question is If the hacker gets access to the database,he will get access to the messagedigests/even if he does not get access to database,he can still obtain message digest while intecepting communication link when user first registers to DB......how this can be prevented??
Even if i do not have the password,i still can intercept the message digest......i dont even need the password ...i will simply launch reply attack(ie. send message digest itself for authentication after intercepting it!!)
This shouldn't be possible.
The client should send the real password to the server. It should be encrypted using SSL.
The server should hash the password and compare it to the stored, hashed password.
the solution to above problem…
That is, more or less, part of what SSL does.
My question is If the hacker gets access to the database,he will get access to the messagedigests
This is a relatively insignificant problem. The passwords should be stored as hashes with varied salts. The original passwords are protected.
even if he does not get access to database,he can still obtain message digest while intecepting communication link when user first registers to DB
Only if SSL is broken.
User registration should always be done through an SSL tunnel, so generally when following best practice you never have to worry about man in the middle type attacks.
I have been looking for a good explanation of how to implement a password login system in a typical website environment. I have read some great wikipedia articles and SO Q&A and blogs etc but they always seem to focus on purely generating the hash rather than the whole process of creating hash sending which parts of it, storing which parts of it, what the server side code does with it etc. If there is already a good answer on SO I apologise for reposting, and please link.
My current understanding is:
1) A new user creates a new account on your website. They enter a "password", the client side code then generates and appends a long random string "salt" to the end and generates a hash -> BCrypt(password+salt) for example. The client code then sends the full hash plus the unhashed salt to the server.
2) The server stores the full hash and the unhashed salt in the users entry in a DB.
3) During the user login they type their password which is then hashed with a salt again,
Question 1) How does the client side code generate the same 'random' salt value for each user?
Question 2) at this point does the client side code just send the full hash without the salt?
Question 3) what does the server side do with the full hash once it has received it? (simply compare the sent full hash with the stored full hash? If that's the case then can't an attacker upon breaking into the db and getting the stored full hash values just use them directly to send to the server to log in? This is based on my assumption that the log in process essentially involves the server comparing the full hash sent from the client with the full hash stored in the db.
Question 4) should passwords always be sent over secure connection? or does salting and hashing them make it ok for anyone to see?
You are confusing the purpose of the hashing. It is not intended to secure the password for wire transmission. The client does not generate the hash. the purpose of the hash is to prevent an attacker who compromises your database from being able to quickly use a pre-generated hash lookup table to determine what your user's passwords are.
A trivial example follows- as #jhoyla points out in the comments below, industrial grade production schemes are even more complex.
To create an account:
The client establishes a secure (encrypted, e.g. SSL) connection with the server, and sends the username and password, usually in plaintext (which is OK, because it is encrypted).
The server generates a random salt, appends it to the password, hashes the result, and stores the hash and the unhashed salt value.
To log in:
The client establishes a secure (encrypted, e.g. SSL) connection with the server, and sends the username and password, usually in plaintext (which is OK, because it is encrypted).
The server retrieves the salt from storage, appends it to the password, hashes it, and compares the result to the hashed password in storage. If they match, the user is logged in.
To establish why we do this, imagine that I have successfully attacked a website's database server and downloaded the database. I now have a list of usernames, probably email addresses, and password hashes. If the passwords are not salted, then there is a very high probability that many of the hashes will be the same (because many people use the same weak passwords). I know that the likelihood of one of those users having that same weak password on (for example) their email account is quite high. So I go to work and hash the whole dictionary, plus many other likely passwords, looking for a hash that matches one of these popular ones. If I get a hit, I've just broken a bunch of passwords. If I was smart, I'd have generated this list in advance so that I can do it quickly.
Now imagine that the passwords are salted. Now, even if two people use the same password, a different salt will have been generated for each of them, and the resulting hashes will be different. I have no way of knowing which passwords are weak, common passwords, and which ones are strong passwords. I can try my dictionary attack by appending the salt to each possible password, but the difficulty (in terms of time) of cracking a password has now gone up exponentially.
never ever implement it yourself! if you need it just for learning then #Chris answered you. but if you need for for a working software then don't do it. every language has security libraries and every data store (ldap, database) has password storing mechanism already implemented. use it, don't invent the wheel again because you will most probably miss some detail
When authenticating a user to a website, should the hash generation and comparison be done in the database or the website?
My argument is the website should pass the user supplied password (possibly encrypted by the web server) to the database. The database then re-encrypts it with the salt and compares the hash's. The database the responds to the web server whether the user's credentials are valid or not. This way, the very minimum ever leaves the database, essentially either a yes or no, none of the stored credential info. Downside is, the database has to do more work.
The other argument is that the work should be done in the web server. Here the web server would create the hash and request the stored hash from the database and compare. In this situation the salt needs to be passed from the database back to the web server for the hash to be created. but, work is shared as # of web servers increase.
Personally I see the second method as a potential security risk. Should the web server be compromised, salts and hashes can be requested from the database and easily cracked.
What is the best practise for performing the above operation? Am I overlooking/missing something?
Thanks
The first problem I suspect you will run into (and it's a big one) is that your database does not have a password hash function. Sure, it probably has MD5() and SHA1() but these are cryptographic hash functions. Does it have bcrypt() or scrypt() or PBKDF2()?
Using a cryptographic hash function rather than a password hash function is what meant that the LinkedIn passwords could be cracked so quickly. If you don't use one of the above functions then you will be similarly vulnerable if your hashes are leaked.
Going on to answer your question assuming that your database does support a password hashing algorithm (using bcrypt simply because I have to pick one). The two alternatives are:
Hashing in the database:
$db->query("SELECT COUNT(*) FROM users WHERE username = '?' AND password = BCRYPT(?, (SELECT salt FROM user WHERE username = '?'))", $username, $password, $username);
if($row['count'] != 1)
{
// Not authenticated. Throw exception.
}
In this case, the raw password is sent to the database and a simple yes or no (1 or 0) is returned. This database communication can be encrypted. The hash and salt are never held in the application.
Hashing in the application:
$db->query("SELECT username, salt, password FROM users WHERE username = '?', $username);
if(bcrypt($password, $row['salt']) != $row['password'])
{
// Not authenticated. Throw exception.
}
In this case, the hash and salt are pulled from the database into the application and the hashing of the raw password and comparison is done there. The communication to the database can still be encrypted. The raw password is never held in the database memory.
For efficiency, we can assume that both hashing algorithms are written in C (or some compiled language) and are possibly provided by the OS so take the same time. The application hashing option receives more data over the wire and the database hashing option sends more and has a more complex query (essentially two queries, one to get the salt and one to effect the comparison). It may not be possible to use an index the way I have written that query but the query could be rewritten. Since the size of the data in both cases is likely still one TCP packet, the speed difference will be negligible. I would call this one a win for the application hashing option due to the subquery.
For exposure. I would consider the raw password to be more sensitive than the hash and the salt together. Therefore, limiting the exposure of the raw password seems like the safer bet, making application hashing the best practice.
There's a really good article on how to store passwords securely here:
http://throwingfire.com/storing-passwords-securely/
You are overlooking the purpose of a salt.
A salt is used to prevent a dictionary attack against hashed passwords. If your password is "peanut" and hashes to 12345, then I can pre-generate a list of hashes for every word in a dictionary (including your password) and quickly find your password by doing a lookup against my pre-generated set of password hashes. This is what happened to LinkedIn recently. If the passwords are salted, I'd have to pre-generate a dictionary for each salt value after compromising the database, which would be prohibitively expensive.
Furthermore, proper randomly-generated salts prevent an attacker from knowing that you and I have the same password (without the salt, we'd have the same hash).
My point is that the salts are not intended to be a secret. They are not public information, but an attacker getting access to the salt values + the hashes does not necessarily mean that the passwords have been compromised.
A good rule of thumb for computer security is that if you have to ask, you shouldn't do it yourself. But if your concern is exposure of password details if the web server is compromised, then one approach is to move authentication onto its own system, and don't give the web server access to the password database at all.