Security protocol for server-side shared secret generation - security

I am trying to implement a security system that has the following requirements:
All clients share a password, which is not known to the server
Each client has a unique client-id, which is known to the server
All clients with knowledge of the password must be able to generate the same shared secret on the server (this secret can be anything, it just needs to be the same for all clients and unique across passwords)
The password needs to remain secure, even if the server or the transport get hacked
It must be impossible for another party with a different client-id to generate the same server-side secret without knowledge of the password
Let me try to give a graphical representation of this:
Client Server
.--------------^-----------. .----------^----------.
f(client-id 1) g(client-id 1)
PASSWORD ----------------> request 1 ----------------> KEY
|| equal || equal
PASSWORD ----------------> request 2 ----------------> KEY
f(client-id 2) g(client-id 2)
Here, f() [g()] are functions that the client [server] applies to the password [request] to obtain the request [key]. These functions may depend on the client-id.
There's two approaches that I have come up with that might do this, but I am hoping for something simpler that requires less traffic and less server load:
"No-brainer": The clients hash the password. The clients and server each use a standard mechanism (like SSL) to secure their connection and send the hash over this connection.
"A little more clever": The server has a fixed private-key coded into it and each client has the public-key coded into it. The clients hash the password, XOR it with their client-id, encrypt the result with RSA/PGP using the public key. The server then decrypts the request using the private key and XORs the result with the client-id to arrive at the password hash.
In both cases, the server ends up with the same secret for the clients: the password hash. The advantage of the second version is that there is no need for the overhead of a full-fledged key exchange and encryption system, since unfortunately I won't be able to rely on SSL in all cases. In fact, it allows me to generate the server secret in a single request without any handshake. The client-id-XOR in the second version are used to prevent replay-attacks, where a third party with a different client-id could otherwise simply send the same encrypted message to the server to generate the same secret. Basically it's a no-overhead way to add a salt.
Now the question:
Since I don't really have any requirements on the server-side secret, not even that the clients can generate this secret locally, is there an even simpler way to do this that doesn't require expensive modular exponentiation of arbitrary-precision numbers like RSA does? I'm thinking of maybe some sort of other trapdoor function for f() and g() above that allows me to achieve the same result.

No takers, I guess... The question is probably too vague...
In any case: For now I've decided to use RSA (i.e. approach 2 from above). It's simple enough to implement and with the right libraries, it's not too expensive to run either.

Related

How to correctly apply client side hashing of passwords?

I'm looking for an established security mechanism to apply client side password hashing in addition to server side password hashing. The client is either a web app or a mobile app. The communication between client and server uses HTTPS. The server should use bcrypt or a similarly secure password hashing algorithm.
Now, I don't want to send the unencrypted password from the client to the server in order to prevent insider attacks against the user. I want to make sure that none of our admins is able to get the original password, since -- as we all know -- most people re-use their password on multiple web sites or use a password scheme to derive passwords that are easy to remember.
Additionally, I would like to implement an additional two-factor-layer of security into the authentication protocol that makes use of a secret stored on the client in order to strengthen the overall security even more.
Are there any published and validated mechanisms that can be used for this situation?
EDIT
In my scenario, evil admins cannot control the code of the app. The server that is doing the authentication and that provides sensitive data is a REST server. The application code is delivered by a different server (for the web app) or via the AppStore (for the mobile app). And I am also thinking about scenarios where an attacker has only read-only access to an internal network segment that connects the HTTPS server with the application server. In most real world setups, SSL termination is done on a dedicated server.
In contrast to a website, your scenario with an app is well suited for client side hashing. The app already contains the code for calculating the hash, and the code cannot be intercepted/altered as this would be the case with JavaScript. Even more, you can relieve the server from the CPU intensive calculation.
These are the necessary steps for registration:
On client side, calculate the hash of the password, using a slow hash function with a cost factor. Appropriate algorithms are BCrypt, PBKDF2 or SCrypt. Most libraries will generate a random salt on their own and include it in the resulting hash-value.
Send the client-hash to the server (you already mentioned the SSL connection).
On server side, again calculate a hash of the sent client-hash. This time you don't need a salt and you can use a fast algorithm like SHA-256. This is safe, because the client-hash is already a very strong "password", it is not possible to forge the client-hash out of the server-hash.
Extract the parameters like salt, cost factor, and algorithm from the client-hash.
Store the server-hash and the parameters (salt, …) in the database.
These are the steps for login:
The client has to ask for the parameters (salt, …), stored in the database on the server.
The client calculates the hash of the password with the same parameters as by the registration.
Send the client-hash to the server.
The server again hashes the client-hash with the fast algorithm and compares it with the stored hash in the database.
This is a bit more complex than sole server-side hashing, because you have to take care of storing the parameters (salt, …) separately.

Is it acceptable to publicly expose salts?

I'm building a REST API for a mobile app. I need to handle authentication. As far as I could find, these are the best practices regarding password storing.
On the client side-register
Let user choose a password
Create a sufficiently long random salt from a strong random number generator
Append the the salt to the password and hash it.
Send the hash and salt to the server and store it in database
Server side-log in
Expose a method that returns user's salt for a given userid(email)
Client side-log in
User types in his userid and password
Retrieve the salt for the given userid
Append the the salt to the password and hash it.
Send the hash to the server, check that it's the same and authenticate user
Obviously the week point in this approach is that an attacker can see the salt for any user effortlessly. Is this ok? Obviously I can store the salt on the client side as well but if e.g. the user is logging in from a different phone he has to be able to retrieve the salt.
The alternative to this is to send the password in plaintext and handle all the salting and hashing on the server side. I would use HTTPS.
Which of these approaches is preferable? Is there a third one I'm missing?
Obviously the week point in this approach is that an attacker can see the salt for any user effortlessly.
No, the weak point is that your server is trusting the client. Here is MSDN's brief and pointed take on the situation:
You should never trust user input directly, especially if the user input is anonymous. Remember the two golden rules: never trust user input, and always check data as it moves from an untrusted to a trusted domain.
Source: Do Not Trust User Input Directly
Here you have user data moving from their untrusted client to your server. You need to verify their input on your server. There is no attack that you can prevent by performing the generation of the salt and the hashing of the password on the client. The ONLY way to secure the server-client communication is with secure handshake key exchange for asymmetric encryption. This occurs over HTTPS, and once the link is secure, then sending the password in plaintext is no less dangerous than sending the code to the client to perform the hashing client side.
If you do not secure the connection to the client with HTTPS, you are always vulnerable to a Man-in-the-Middle attack. Even though the password is nominally obscured when returning to your server, an attacker could instead impersonate your server to the client, sending an HTML form that skips the hash or sends the password in plaintext to their server as well.
Exposing the salt in your scenario is an additional security hole, but it is minor next to the glaring hole that would allow an attacker to get the password directly. I can explain this attack further if you desire, but it is a totally pointless attack unless the password itself is better secured; and once that happens, you have no need to expose the salt in the first place.
Responding to comments, it is true that salts can be made publicly known without reducing security if you are certain that they were generated correctly. For a salt, that means they are of sufficient length, sufficiently random, and in some schemes that they are cryptographically secure. The problem is that you allow the client to generate the salt, which means, I think we all agree now, that they can't be trusted. Maybe an attacker corrupted an external library you're relying on for PRNG. Maybe some user devices have an insufficient entropy pool. Maybe their device isn't secure against side-channel attacks. If you're making an Android App, there are certainly some very insecure devices out there. In Apple's walled garden, these risks might be tolerably low, but you can never eliminate them.
Like I said originally, however, these risks are minuscule next to the real dangers of your scheme. Namely, you're transmitting the hashed passed in the clear to your server. Even if you hash it further before storing it in your database, so a SQL injection can't just dump all the user passwords, you're still vulnerable to Man-in-the-Middle attacks. Unencrypted HTTP traffic can be observed and recorded by any router it passes through. Once an attacker observes a hashed password, they can re-send the same request to your server, and be authenticated incorrectly.
You should strictly go for the alternative, "to send the password in plaintext and handle all the salting and hashing on the server side. I would use HTTPS."
Of course the sentence above is highly contradictory: if you would be using HTTPS then the password would not be in plaintext. You should use HTTPS.
At the server you can store the randomly generated salt as plaintext in the database. You should not opt for a normal hash but a password hash - such as one generated by PBKDF2 - instead.
The salt is there if your DB gets stolen: the salt will make sure that identical passwords are not revealed to be identical. Furthermore the salt will make it impossible to use rainbow tables with precomputed password hashes.
Two important things to notice: for web clients you will have to rely on the trusted TLS certificate store for creating a secure TLS connection. Furthermore, your client may not have access to a good random number generator.

Is my implementation of one-time password safe enough?

I want to implement a login mechanism for authenticating the administrator of my web app. But since I am not using ssl I cannot rely on user sending his password through an html form.
So I thought about using one-time password (I actually didn't know about this thing, I ended up reinventing the concept and eventually found out such a thing already existed)
All I want is your opinion on how safe my implementation is:
1) Client requests a random and unique hash. The server generates that hash, stores it in a session variable and returns the hash to the client.
2) From that hash the client derives a password using a custom algorithm that is known only by him and the server. That password is sent back to server.
3) Server does the same and check to see if passwords match, if they do the user is authenticated.
Can this be cracked at all?
"custom algorithm that is known only by him and the server". This is broken by design, since you are relying solely on the algorithm being secret.
It won't be a secret anymore as soon as you distribute the implementation (e.g. as client side JavaScript), let alone any intrinsic weakness in the protocol.
My 2cents: unless you are a crypto expert, never roll out your own solution. Stick with public, sound and proven protocols and only use reputable implementations.

Application to Application authentication/access control without HTTPS

On the current project I'm working on, we have the following problem.
Our software is a Location Based Services platform and applications can connect and use our services through our exposed webservices using SOAP. Until now our platform was only used by internal applications, but now we would like to open it for third party applications. For that we need an authentication mechanism.
Because of our customers' infrastructure and load balancing solution, we cannot use HTTPS. The original idea was that applications can just use HTTPS and send the password we authenticate.
A solution would be the next:
The application has the password. The application generates a random string (salt) and creates a hash. Then the application creates an HTTP request sending the hash, the salt and a timestamp. This three is enough for us to authenticate, as we can generate the same hash and compare.
My problem is that for this we need to store the password in our database in clear text, because we need to do the same process using the given salt so we can compare the result and authenticate the application. Storing passwords in clear text is unacceptable.
Do you know about any authentication/access control mechanism that would fit this situation? Generally, do you know about any good books/sources about application authentication/access control mechanisms?
Any help is highly appreciated. Thanks in advance!
The application (client) can hash the password two times. Note that the server should generate the other random salt, not the client! Otherwise the attacker can log with this hash also. You can also make it safer by storing password specific salt in the database.
The protocol:
0) servers retrieves salt for that particular password from database, generates salt2, and sends both to the client
1) client sends hash(hash(password, salt), salt2, timestamp) and timestamp.
2) server retrieves hash(password, salt) from the database and compares.
Note that if you are on the network where attackers can not only sniff, but also modify the traffic, as Paulo pointed out, you should sign EVERY message: hash(hash(password, salt), salt2, timestamp, message) and check it at server. (E.g. for the case when the attacker could modify the message to contain delete command...)
Note that there is still a problem when user needs to SET/CHANGE the password safely. You cannot do it safely with just hash function over unsafe network, you need some kind of cipher/decipher.
Also note that the slower the hash function is, the safer (because of the dictionary attack). If you don't have access to special slow hash function, you may also call normal fast hash function 100000 times.
Instead of inventing your own solution, you should use an established one. SOAP has support for cryptographic authentication such as WS-Security - see Craig Forster's comment on this answer for suggestions.
The best choice in other cases is usually oauth; it provides both authorization and authentication, and deals with a lot of cryptographic issues that you're not likely to spot when building your own.
Using an authentication solution which does not contain an integrity check of the whole message (or stream) is insecure.
While the hashing solution originally proposed by Thomas T. (hash(hash(password, salt), salt2, timestamp), where hash(password, salt) is stored in the database, and salt2 is newly generated) makes sure than an attacker can't get the password (or any data which will be useful for logging in after the timestamp expires), it alone does not prevent an active attacker to hijack the session after the authentication, and send any SOAP requests wanted (and intercept the responses).
What would be needed here is some way to make sure that no data is changed. This is known as a message authentication code (MAC). The usual definition of a MAC includes some (shared secret) key and the message as input, and an authentication token as output.
The usual way to use this would be to do some authenticated key exchange at the beginning of the communication (using either a shared secret or some known public key), and then use a part of the now shared secret as the MAC key, which is then used to authenticate following messages.
(Doing this then essentially is a reinvention of SSL/TLS (or parts thereof), potentially doing the same mistakes again.)
If you have only one message to send, you can instead use the MAC as a kind of symmetric signature, using the password hash (salted and generated with a slow hash function) as a MAC key.
Another way to view this would be to take the message to authenticate as an input to the outer hash in Thomas T's authentication scheme. (Make sure to authenticate everything that is worth authenticating.)

Should I hash the password before sending it to the server side?

I noticed that most sites send the passwords as plain text over HTTPS to the server. Is there any advantage if instead of that I sent the hash of the password to the server? Would it be more secure?
This is an old question, but I felt the need to provide my opinion on this important matter. There is so much misinformation here
The OP never mentioned sending the password in clear over HTTP - only HTTPS, yet many seem to be responding to the question of sending a password over HTTP for some reason. That said:
I believe passwords should never be retained (let alone transmitted) in plain text. That means not kept on disk, or even in memory.
People responding here seem to think HTTPS is a silver bullet, which it is not. It certainly helps greatly however, and should be used in any authenticated session.
There is really no need to know what an original password is. All that is required is a reliable way to generate (and reliably re-generate) an authentication "key" based on the original text chosen by the user. In an ideal world this text should immediately generate a "key" by salting then irreversibly hashing it using an intentionally slow hash-algorithm (like bcrypt, to prevent Brute-force). Said salt should be unique to the user credential being generated.
This "key" will be what your systems use as a password. This way if your systems ever get compromised in the future, these credentials will only ever be useful against your own organisation, and nowhere else where the user has been lazy and used the same password.
So we have a key. Now we need to clean up any trace of the password on the clients device.
Next we need to get that key to your systems. You should never transmit a key or password "in the clear". Not even over HTTPS. HTTPS is not impenetrable. In fact, many organisations can become a trusted MITM - not from an attack perspective, but to perform inspections on the traffic to implement their own security policies. This weakens HTTPS, and it is not the only way it happens (such as redirects to HTTP MITM attacks for example). Never assume it is secure.
To get around this, we encrypt the key with a once off nonce.
This nonce is unique for every submission of a key to your systems - even for the same credential during the same session if you need to send it multiple times. You can reverse said nonce (decrypt), once it arrives in your own systems to recover the authentication key, and authenticate the request.
At this point I would irreversibly hash it one last time before it is permanently stored in your own systems. That way you can share the credential's salt with partner organisations for the purposes of SSO and the like, whilst being able to prove your own organisation cannot impersonate the user. The best part of this approach is you are never sharing anything generated by the user without their authorisation.
Do more research, as there is more to it than even I have divulged, but if you want to provide true security to your users, I think this method is currently the most complete response here.
TL;DR:
Use HTTPS.
Securely hash passwords, irreversibly, with a unique salt per password. Do this on the client - do not transmit their actual password. Transmitting the users original password to your servers is never "OK" or "Fine". Clean up any trace of the original password.
Use a nonce regardless of HTTP/HTTPS. It is much more secure on many levels. (Answer to OP).
Since it's over HTTPS, it's definitely just fine to send the password without hashing (over HTTPS it's not plaintext). Furthermore, if your application is depending on HTTPS to keep it's content secure, then it's useless to hash the password before sending it over HTTPS (i.e. if an attacker can unencrypt the data on the wire, you're screwed anyways)
No, in fact this would be a vulnerability. If the attacker is able to obtain the hash from the database, then he could use it to authenticate without needing to crack it. Under no circumstance should a user or an attacker be able to obtain a hashes password.
The whole point of hashing passwords is to add an extra layer of security. If an attacker is able to obtain the hash and salt from the database using SQL Injection or an insecure backup then he has to find the plain text by brute forcing it. John The Ripper is commonly used to break salted password hashes.
Not using https is a violation of the OWASP Top 10: A9-Insufficient Transport Layer Protection
EDIT:
If in your implementation you calculate a sha256(client_salt+plain_text_password) and then calculate another hash on the server side sha256(server_salt+client_hash) then this is not a serious vulnerability. However, it is still susceptible to eavesdropping and replaying the request. Thus this is still a clear violation of WASP A9. However, this is still utilizing a message digest as a security layer.
The closest thing i have seen to a client-side replacement for https is a diffie-hellman in key exchange in javascript. However, this does prevent active MITM attacks and thus is till technicality a violation of OWASP A9. The Authors of the code agree that this is not a complete replacement for HTTPS, however it is better than nothing and better than a client-side hashing system.
Sending a hash over the wire completely defeats the purpose of the hash, because an attacker can simply send the hash and forget about the password. In a nutshell, a system that athenticates using a hash in clear text is wide open and can be compromise with nothing more than network sniffing.
The password in plaintext show never (not even when using HTTPS) leave the client. It should be irreversibly hashed before leaving the client as there is no need for the server to know the actual password.
Hashing then transmitting solves security issues for lazy users that use the same password in multiple locations (I know I do). However this does not protect your application as a hacker that gained access to the database (or in any other way was able to get his hands on the hash) as the hacker could just transmit the hash and have the server accept it.
To solve this issue you could of course just hash the hash the server receives and call it a day.
My approach to the issue in a socket-based web application I'm creating is that on connection to the client the server generates a salt (random string to be added before hashing) and stores it on the sockets variable, then it transmits this hash to the client. The client takes the users password, hashes it, adds the salt from the server and hashes the whole thing, before transmitting it to the server. Then it's sent to the server which compares this hash to the hash(hash in the DB + salt). As far as I know this is a good approach, but to be fair I haven't read a lot on the topic and if I'm wrong about anything I'd love to be corrected :)
Disclaimer: I'm by no stretch a security expert-- and I'm posting with the hope that others will critique my position as overly cautious or improvable and I will learn from it. With that said, I just want to emphasize that hashing when it leaves your client doesn't mean you get to don't have to hash on the backend before putting it in the database.
Do both
Do both because:
Hashing on the ride over helps cover vulnerabilities of transport, if SSL connection is compromised, they still can't see the raw password. It won't matter in terms of being able to impersonate authorized users, but it will protect your users from having their passwords read in association w/ their email. Most people don't follow best practice and use the same password for many their accounts, so this can be a serious vulnerability to your visitors.
If someone, somehow was able to read passwords from the database (this does happen, think SQL injection), they still won't be able to execute privileged actions impersonating users through my API. This is because of hash asymmetry; even if they know the hash stored in your DB, they won't know the original key used to create it and that's what your auth middleware uses to authenticate. This is also why you should always salt your hash storage.
Granted, they could do a lot of other damage if they had free rein to read what they want from your database.
I just want to emphasize here that if you do decide to hash the key before departure from your clients, that isn't enough-- the backend hashing is, imo, much more important and this is why: If someone is intercepting traffic from your client, then they will see the contents of the password field. Whether this is a hash, or plain text, it doesn't matter-- they can copy it verbatim to impersonate an authorized client. (Unless you follow the steps which #user3299591 outlines, and I recommend you do). Hashing the DB column, on the other hand, is a necessity and not at all difficult to implement.
Use HTTP Digest - it secures the password even over http (but best useage would be http digest over https)
Wikipedia:
HTTP digest access authentication is one of the agreed methods a web server can use to negotiate credentials with a web user (using the HTTP protocol). Digest authentication is intended to supersede unencrypted use of the Basic access authentication, allowing user identity to be established securely without having to send a password in plaintext over the network. Digest authentication is basically an application of MD5 cryptographic hashing with usage of nonce values to prevent cryptanalysis.
Link: http://en.wikipedia.org/wiki/Digest_access_authentication
If you want to see a "real life" use, you could look at phpMyID - a php openid provider that uses http digest authentication http://siege.org/phpmyid.php
.. or you could start from the php auth samples at http://php.net/manual/en/features.http-auth.php
Http digest rfc: http://www.faqs.org/rfcs/rfc2617
From my tests all modern browsers support it...
If you're looking to replace a clear-text password over HTTPS with a hashed password over HTTP then you're asking for trouble. HTTPS generates a random, shared transaction key when opening up a communication channel. That's hard to crack, as you're pretty much limited to brute forcing the shared key used for a (relatively) short-term transaction. Whereas your hash can be just sniffed, taken off-line and looked up in a rainbow table or just brute forced over a long amount of time.
However, a basic client-side password obfuscation (not hash) sent over HTTPS does have some value. If I'm not mistaken this technique is actually used by some banks. The purpose of this technique is not to protect the password from sniffing over the wire. Rather, it's to stop the password from being usable to dumb spying tools and browser plug-ins that just grab every HTTPS GET/POST request that they see. I've seen a log file captured from a malicious website that was 400MB of random GET/POST transactions captured from user sessions. You can imagine that websites that used just HTTPS would show up with clear-text passwords in the log, but websites with very basic obfuscation (ROT13) as well would show up with passwords that are not immediately of use.
Whether there's an advantage, and whether it's more (or less) secure really depends on implementation. There's arguably some advantage, but if you implement it poorly, you could definitely create a solution that is less secure than passing even a plaintext password.
This can be looked at from the perspective of two types of attacks-- one with access to the network traffic, and another with access to the database.
If your attacker can intercept the plaintext version of the network traffic, then seeing a hash of the password is more secure than seeing the password in plaintext. Although the attacker could still log in to your server using that hash, it would require a brute-force crack (sometimes pre-computed) of that hash to determine the password that might be useful on other systems. People should use different passwords on different systems, but often don't.
If an attacker gained access to the database, perhaps through a copy of a backup, then you'd want to ensure that one couldn't log in with only that knowledge. If, for example, you stored a hash salted with the login name as hash(login_name+password), and you passed that same hash from the client for direct comparison, then the attacker could pick a user at random, send the hash read from the database and log in as that user without knowing the password, increasing the scope of the breach. In that case, sending the password in plaintext would have been more secure because the attacker would need to know the plaintext in order to log in, even having a copy of the database. This is where implementation is key. Whether you send a plaintext password or a client-side hash of that password, you should hash that value at the server-side and compare that hash with the hash stored in the user record.
Concepts to keep in mind:
You "salt" a hash by mixing in some scope-unique value to your hash, typically row-unique. Its purpose is to guarantee uniqueness of hashes from each other even if the plaintext values they represent are the same, so two users with the same password would still have different hashes. It's unnecessary to treat a salt as a secret.
When authenticating, always hash on the server-side whatever value you pass from the client as a password (even if it's already hashed) and compare it with a pre-hashed value stored on the database. This may necessitate storing a double-hashed version of the original password.
When you make a hash, consider adding a server/cluster-unique salt to the hash as well as a row-unique salt to safeguard against matching any pre-computed hashes in lookup tables.
If you're connected to an https server the data stream between the server and browser should be encrypted. The data is only plain text before being sent and after being recieved. Wikipedia article
If you want to achieve the same reliability as when transferring over https, then yes - one option - the implementation of an asymmetrically encrypted channel at the ajax level.
If we are not talking about registration (for example, the first transmission of the password is always protected), then there are options.
For example,
The server generates a random string and generates a salt sends it to the user.
The user calculates a hash from his password and using this hash as
a key encrypts this random string with a blowfish, for example (there
is an implementation in JS for sure) and sends it back to you.
You, on your own, using the hash stored on the server, also encrypt this
random string with a blowfish.
Compare.
An attacker would have to attack the bluefish key using a random source and ciphertext. The task is not easy.
Isn't SSL/TLS replacing the nonce? I don't see added value of this since SSL/TLS also protects against Replay Attacks.
Ref.
https://en.wikipedia.org/wiki/Cryptographic_nonce
It would actually be less secure to hash the password and send it over a non-encrypted channel. You will expose your hashing algorithm on the client. Hackers could just sniff the hash of the password and then use it to hack in later.
By using HTTPS, you prevent a hacker from obtaining the password from a single source, since HTTPS uses two channels, both encrypted.

Resources