I am trying to create a login system for a web application, but I am stuck on a couple of points. I am storing the password in my database using a sha2-512 hash with a 128 bit random salt.
However I currently have the password posted in plain text to my application using a html form, both when the account is created and when the user logs in. I know this is wrong.
Do I need to hash the password in the client? If so how do I take into account the salt which is currently generated and stored on the database?
NOTE: I am doing this to learn not to use in a production system
The best bet is generally just to use SSL. If you did need to hash on the client side, this is how I'd do it:
When you first store the password, hash the password with a stored salt as is commonly done.
When someone needs to login, send them the stored salt, along with a second, randomly generated salt.
The client will hash the plaintext password with the stored salt, then the random salt and send the hash to the server.
The server will hash the stored password with the random used in that request salt and compare.
This is secure because it ensures that the hash being transmitted is unique to the request (it uses a single-request random salt), so a login cannot be faked in the future simply by sending the hash again. It is not dangerous to send the client their stored salt, as it is assumed that password crackers will have access to the stored salt (if they get access to the db). Two hashes are required to prevent you from ever having to store the password as plaintext.
You should be using SSL to transmit the passwords encrypted so that a man-in-the-middle can't intercept the packets and read off what ever credential is being sent. Even if you pre-hash the password in the client, a man-in-the-middle can still just use that value to fake identity.
What really concerns me, though, is the use of SHA-512. A lot of people use cryptographic hashes for password storage, but popular opinion misses a very important point: These hashes were designed to be fast. That is, one of the requirements to become an SHA (or similar) hash is to be able to quickly hash large documents on embedded hardware.
This is the exact opposite of what you want for password storage, as it allows specialized routines on high performance GPUs to brute force passwords at a surprising and scary speed!
This is why some purpose built password storage hashes have been developed. The one I have been using is Bcrypt, which is slow enough to keep out brute force attacks, adjustable to couneract faster hardware in the future, and has the added bonus of handling the salting for you.
Hashing the password on the client would require the use of the salt on the client. This also exposes your algorithm for very easy hacking on the client side. The best thing to do is to perform this action over SSL (HTTPS) so that the entire transaction is encrypted and the authentication only happens on the server.
I.e.: Your user ID and password are transmitted encrypted from the client. The web server decrypts the data and passes it to your server-side authentication function where you look up the user and associated salt, perform password + salt + hash and compare it to the stored hash for a match. This means that the hash and then salt never need to be transmitted from the server at all.
You really need to be using SSL on any page where you are transmitting passwords. If you try to encrypt them on the client side it will be in javascript and very easily reverse-engineerable.
Related
I've been trying to research authentication systems to model my own project around, but I feel like I'm misunderstanding something significant.
There is a lot of talk about using a per-user salt to hash the password, and this makes sense as a defence mechanism. But almost all info I see about this is done on the server side (back end). This implies that the front end is sending the password as plaintext, which I thought was a no-no.
Then I see discussion of salting the password at the front end (client-side before sending the login to the backend for authentication). This seems to solve the problem of sending the password in plaintext, but introduces the problem that the password must be hashed with the same salt each time in order to match the hashed password stored in the back end. This implies either a site-wide salt, or that the front end is getting access to the user salt.
The latter seemed feasible for a second, but this seems the same as publishing the per-user salt, as the front end needs to access the salt prior to authentication.
Can anyone tell me what I am not understanding?
Salting the password, before it is hashed and saved to the DB, is meant for better protection of the saved password. If someone manages to steal your password DB, when passwords are salted, it will be much harder for them to reverse the hash and get the raw password value.
This has nothing to do with the security of passwords on the front channel. You can send passwords in plain text from your front channel to the backend if you use TLS. The request is encrypted and thus protected from eavesdroppers. You will still be vulnerable if you have a man-in-the-browser or an XSS attack, as the attacker will have access to the plain-text password before TLS encryption is applied. However, hashing the password in the frontend app will most probably not protect you from these attacks anyway.
To sum up:
You can send plain-text passwords from your front end, just make sure to use TLS.
On the back end, make sure to salt and hash passwords before saving them in the DB.
I'm currently making some research on the WSSE protocol to secure my api.
I've understood the basis and it is clear in my head.
However I face to a problem.
Let's say I want to generate a ONE-time Digest, I've to concatenate the timestamp, the nonce and the secret password.
However, on the server side, I don't have the clear password stored in database, so I'm unable to check for the digest.
Switching to clear password is not an option.
I can't either provide to the client-side the algorithm used to hash password, because they use salt, and the salt is not available to the client.
The only solution I can think about is to don't hash the Digest, just concatenate the data and base64 them.
That way I'll be able to encode the user password to check for its validity.
As password is transmited in plain text it may not be secure, but what if I force HTTPS to authenticate?
Will it be safe?
As password is transmited in plain text it may not be secure, but what if I force HTTPS to authenticate?
Will it be safe?
Yes, and within normal limits, it is the only way to get it secured.
Just send the full password, but send it over a well secured TLS connection. You can use a secure key derivation function such as brypt or PBKDF2 and input the stored random salt (one per user) and password to retrieve the "hash", which can then be checked to the one stored in the database.
Note that you don't need to send a time stamp from the client. It is of course a good idea to log & limit the number of authentication attempts though. You cannot use a time stamp as input to a hash, the outcome would be different each time the time stamp changes.
I'm trying to secure a website that is being moved to a public server soon. I've just finished adding the password hashing functions to all of my login scripts. I'm using FormsAuthentication.HashPasswordForStoringInConfigFile(pw, method) to do so. I have a question about the process I'm using and whether or not it's secure for a web server:
Password is sent in plain text over HTTPS to the server
The server looks in the Users table to find the user's Salt (several random characters) and their hashed and salted stored password
The plain text password is appended with the Salt
The new string is hashed using the above function
The newly hashed version is compared to the stored version
If equal, login is allowed
If not equal, the login attempt is logged in Session variables, up to 3 times before locking out the user's machine from accessing the login page until an admin verifies IP address and unlocks.
Does this look about right? I just don't see how the salt is effective in this method... Anyway, all I've done is add a salt and hash. Is this considered Encryption? Or am I missing a step? I remember reading that hashing algorithms like SHA1 and MD5 are not encyption algorithms, so what else needs to be done?
That is correct. The salt is used to prevent rainbow table attacks where a dictionary of common works hashed with MD5 is used to try to gain entry. Using the salt ensures that even if they had an MD5 hash of the word, it wouldn't work because they don't know the salt.
The MD5 algorithm is a 1 way hash algorithm, and not an encryption value. The difference is, once you've hashed the value, there is no way to get back to the original value. Encryption allows you to decrypt the data and get back the original value. So you are correct, they are not the same, and your passwords are not encrypted, they are hashed. This means that if someone forgets their password, you cannot send it to them. You have to provide a way for them to reset their password instead. This also means that anyone with access to the database would not have access to raw passwords. Which is good because a lot of people use the same password everywhere, and if you had access to a large list of usernames and passwords, someone could decide to start trying to log into bank / credit card websites.
What you are doing is a recommended practice.
You shouldn't be storing the retry count in the session - an attacker could simply discard their session cookie after each attempt, allowing them to retry as many times as they wish. Instead, store it against the user record.
Being unable to locate a working php/javascript implementation of blowfish, I'm now considering using SHA1 hashing to implement web-based authentication, but the lack of knowledge in this particular field makes me unsure of whether the chosen method is secure enough.
The planned roadmap:
User's password is stored on the server as an MD5 hash.
Server issues a public key (MD5 hash of current time in milliseconds)
Client javascript function takes user password as input, and calculates its MD5 hash
Client then concatenates public key and password hash from above, and calculates SHA1 of the resulting string
Client sends SHA1 hash to the server, where similar calculations are performed with public key and user's password MD5 hash
Server compares the hashes, a match indicates successful authentication.
A mismatch indicates authentication failure, and server issues a new public key, effectively expiring the one already used.
Now, the problematic part is about concatenating two keys before SHA1, could that be prone to some kind of statistical or other attacks?
Is there any specific order in which keys should be concatenated to improve the overall quality (i.e. higher bits being more important to reliability of encryption)?
Thank you in advance.
If you're only using the 'public key' (which isn't actually a public key, it's a nonce, and should really be random, unless you really want it to be usable over a certain timeframe, in which case make sure you use HMAC with a secret key to generate it so an adversary cannot predict the nonce) to prevent replay attacks, and it's a fixed size, then concatenation might not be a problem.
That said, I'm a bit concerned that you might not have a well-thought-out security model. What attack is this trying to prevent, anyway? The user's password hash is unsalted, so a break of your password database will reveal plaintext passwords easily enough anyway, and although having a time-limited nonce will mitigate replay attacks from a passive sniffer, such a passive sniffer could just steal the user's session key anyway. Speaking of which, why not just use the session key as the nonce instead of a timestamp-based system?
But really, why not just use SSL? Cryptography is really hard to get right, and people much smarter than you or I have spent decades reviewing SSL's security to get it right.
Edit: If you're worried about MITM attacks, then nothing short of SSL will save you. Period. Mallory can just replace your super-secure login form with one that sends the password in plaintext to him. Game over. And even a passive attacker can see everything going over the wire - including your session cookie. Once Eve has the session cookie, she just injects it into her browser and is already logged in. Game over.
If you say you can't use SSL, you need to take a very hard look at exactly what you're trying to protect, and what kinds of attacks you will mitigate. You're going to probably need to implement a desktop application of some sort to do the cryptography - if MITMs are going around, then you cannot trust ANY of your HTML or Javascript - Mallory can replace them at will. Of course, your desktop app will need to implement key exchange, encryption and authentication on the data stream, plus authentication of the remote host - which is exactly what SSL does. And you'll probably use pretty much the same algorithms as SSL to do it, if you do it right.
If you decide MITMs aren't in scope, but you want to protect against passive attacks, you'll probably need to implement some serious cryptography in Javascript - we're talking about a Diffie-Hellman exchange to generate a session key that is never sent across the wire (HTML5 Web storage, etc), AES in Javascript to protect the key, etc. And at this point you've basically implemented half of SSL in Javascript, only chances are there are more bugs in it - not least of which is the problem that it's quite hard to get secure random numbers in Javascript.
Basically, you have the choice between:
Not implementing any real cryptographic security (apparently not a choice, since you're implementing all these complex authentication protocols)
Implementing something that looks an awful lot like SSL, only probably not as good
Using SSL.
In short - if security matters, use SSL. If you don't have SSL, get it installed. Every platform that I know of that can run JS can also handle SSL, so there's really no excuse.
bdonlan is absolutely correct. As pointed out, an adversary only needs to replace your Javascript form with evil code, which will be trivial over HTTP. Then it's game over.
I would also suggest looking at moving your passwords to SHA-2 with salts, generated using a suitable cryptographic random number generator (i.e. NOT seeded using the server's clock). Also, perform the hash multiple times. See http://www.jasypt.org/howtoencryptuserpasswords.html sections 2 and 3.
MD5 is broken. Do not use MD5.
Your secure scheme needs to be similar to the following:
Everything happens on SSL. The authentication form, the server-side script that verifies the form, the images, etc. Nothing fancy needs to be done here, because SSL does all the hard work for you. Just a simple HTML form that submits the username/password in "plaintext" is all that is really needed, since SSL will encrypt everything.
User creates new password: you generate a random salt (NOT based off the server time, but from good crypto random source). Hash the salt + the new password many times, and store the salt & resulting hash in your database.
Verify password: your script looks up salt for the user, and hashes the salt + entered password many times. Check for match in database.
The only thing that should be stored in your database is the salt and the hash/digest.
Assuming you have a database of MD5 hashes that you need to support, then the solution might be to add database columns for new SHA-2 hashes & salts. When the user logs in, you check against the MD5 hash as you have been doing. If it works, then follow the steps in "user creates new password" to convert it to SHA-2 & salt, and then delete the old MD5 hash. User won't know what happened.
Anything that really deviates from this is probably going to have some security flaws.
When sending passwords via UTF-8 encoded socket transfer, is it considered to be secure if I hash the password using either MD5 or SHA-1 prior to sending out the data? Keep in mind that I plan to compare the hashed password in a SQL database. I am worried that someone could be able to sniff the hashed password in UTF-8 then decrypt the UTF-8 encoding and could obtain my hashed password which could potentially be used to match the password in my database.
If the client just sends the hashed password, then the hashed password is the "password": a sequence of bytes which the client just needs to show to be authenticated. If the attacker can sniff that then your protocol is doomed.
If the authentication protocol consists in just presenting a piece of secret data (call it a password if you wish), then the exchange should occur within a transport medium which ensures confidentiality (so that the secret data cannot be sniffed) and server authentication (so that an attacker may not mimic a server and convince a client to send him the secret data). This is what you get out of a classic SSL/TLS tunnel (a https:// URL, in a Web context).
If you cannot establish a SSL/TLS tunnel with server authentication (i.e. the server has a certificate which the client can verify), then you may want to resort to an authentication protocol with a challenge: the server sends a sequence of random bytes (the challenge) and the client responds with a hash value computed over the concatenation of the password and the challenge. Do not try this at home! It is very difficult to do it right, especially when the attacker can intercept communications (active attacks).
A more generic answer is password-authenticated key exchange protocols. PAKE combines a cryptographic key agreement protocol (such as Diffie-Hellman) and mutual password authentication between client and server, in a way which defeats both passive and active attackers, even with relatively weak passwords (the attacker cannot get enough data to "try" passwords without interacting with either the client or the server for each guess). Unfortunately, few PAKE algorithms have been standardized beyond mathematical description, and the area is a patent minefield.
Well, if someone can sniff hash - he can fake authorization request and send the hash he already know.
Making up secure system is not easy, you would need to do authorization using asymmetric cryptography with properly signed keys to make it secure.
At least add ~100byte random salt, and use SHA1 - this way it would be way harder to bruteforce.
They could brute-force your passwords if they know the hashing algorithm. The simple (and not perfectly secure) solution is to use a challenge/response instead, the server issues a random string ("nonce") to be hashed along with the password hash. This makes your app invulnerable to the kind of replay attacks you're describing.
For more information, see HTTP's digest access authentication
Hm, if you are talking about 'proper' hashing, that means that it will 'encrypt' your password so it won't be decrypt-able, because hashing is one way function, and to decrypt it - it till take some time, and some kind of great CPU power.
If you are concerned at password sniffers, you can take it to the next level - use PRIVATE/PUBLIC key encryption. Server should send a challenge to the client (public key for encryption), client encrypts with it, and only server know how to decrypt it. For same amount of bits, it offers more protection - ie. more muscle is needed to brute force crack it.
Check this out.
How do you check the password on the database side?
If you store the unsalted hash of the password and just compare it to the input, then the hashed password can be sniffed and reused.
It's exactly as if you were storing the password itself in the database in plain text.
If you are afraid of sniffing, use a challenge-response protocol to authenticate, but in this case the secret will be stored in the database (and will be known to anyone who has access to the database).
Alternatively, you can send a password in plain text over a protected channel (SSL), but you will have to install a certificate which will most probably cost you some money (if you are using an authority from a vendor-provided list, i. e. one your customers' browsers won't complain about)