How can we validate login credentials at the client-side itself without involving the server of a website?
-
35Never trust the client! https://blog.securityinnovation.com/blog/2011/07/do-not-rely-on-client-side-validation.html – Jun 21 '19 at 07:40
-
2These are two independent questions. Please ask these separately. – Steffen Ullrich Jun 21 '19 at 12:05
-
2I once took a free trial for a SaaS application that did this. As soon as I realised what they were doing, I stopped using it and informed the developers with a rather angry tone. Please, just don't. – Darren H Jun 22 '19 at 07:19
-
10This feels like an A-B question. Maybe if you explain why you are trying to do it this way, someone will be able to find you a better solution that doesn't involve client side pseudo-authentication – Darren H Jun 22 '19 at 07:20
3 Answers
You can't.
The reason is that you can't trust the client at all. An attacker can modify the client as they wish, and circumvent any and all security measures you may have put in place.
But what if we digitally sign our code? The attacker can't modify it then, right?
Yes, they can. If you sign your code, the machine of the attacker needs to validate the signature and refuse to run it if the signature of the client does not match. Nothing stops the client from disabling this signature check and simply run code with a wrong signature or no signature at all.
Furthermore, if you don't want to involve the server at all after sending the website, then all the potentially confidential content needs to be sent to the client first (before knowing if they are authorized to see it), and later revealed to them.
Nothing stops an attacker from simply looking at the raw content being sent to them over the network, without any client-side code being run.
But can't you encrypt the data with the user credentials?
Yes, you could. But your goal is to authenticate the user, which means you confirm if the user is actually who they claim to be. The scheme suggested by user9123 would work as follows:
- User claims to be user "foo".
- Website encrypts payload for "foo" with credentials for that user, e.g. "foo:bar".
- User enters their credentials, which locally decrypt the payload.
This scheme does not authenticate the user to the server in any way. The server does not know if the user is really "foo" or not. Furthermore, if the user has a weak password, the attacker can attempt to crack it. Yes, a key-derivation function can make this process slow, but it is still essentially a credential leak.
What I am curious is why you would want to attempt this scheme, instead of the traditional tested method?
-
6@sanjay And, for that last paragraph, there's barely any point encrypting the "_raw content_" since – with no further server contact – you'd also have to send the means of decrypting the data. – TripeHound Jun 21 '19 at 14:10
-
7@TripeHound what if the decryption keys are (generated from) the login credentials? – user49822 Jun 21 '19 at 18:49
-
1@user49822 Then the client has the *unencrypted* code to generate the keys and knows all the inputs necessary for that function. On a truly fundamental level your machine cannot run encrypted code, **just as you cannot perform orders in an encrypted message without decrypting it first.** – l0b0 Jun 21 '19 at 21:57
-
2@l0b0 While I had the same knee-jerk reaction as you, user49822 is suggesting using the actual username and password to encrypt the content. In that case, only if the user is the authorized user can he know how to decrypt the content. – JoL Jun 22 '19 at 00:52
-
2@user49822 If the decryption keys are login credentials, then you have unencrypted passwords on server. So this is still NO-GO! – rsm Jun 22 '19 at 03:27
-
@rsm you could use a hash as the key instead, and have the client implement the same hash – Οurous Jun 22 '19 at 04:09
-
-
@Ourous OK but you still don't know, who has logged in. So which user hash do you use to encrypt data? – rsm Jun 22 '19 at 05:29
-
(This is meant to be a comment on MechMK1's answer but I don't have enough reputation) What about encrypting the login-protected information with the login details? Say, hash a string like "[correct-username] + [correct-password]" and use it as a key to encrypt the protected content on the server and send it over the network. That way, the content cannot be accessed unless the correct login + password pair was entered while also not storing any credentials in the client code or sending them over the network. – user9123 Jun 21 '19 at 18:42
There is a long history of software with client side authentication. The best example is probably operating systems. Microsoft Windows authenticates users with an NT Hash in a special file called SAM; Linux authenticates users with crypt and the /etc/shadow file.
The design is:
- Receive the identifier from the user
- Securely receive the password from the user
- Look up the username and receive the salt and rounds for that user (optionally, reserve or create dummy user settings for usernames not found)
- Generate the hash of the password, using the salt, rounds and pepper
- Compare the generated hash against the stored user hash
- Check whether the user is locked out or other criteria
- Log the user on or fail the login, based on the criteria
This is the same workflow that works in the web paradigm. But steps 3-7 work on the server and not on the client.
- 241
- 1
- 7
-
There’s a slight difference here - in that this only works for unprivileged users, who don’t have elevated access to the ‘code’ that is running. If a user is able to authenticate as an Administrator/root, then they’re essentially ‘the server’ and have access to everything, including other users’ data. – Tim Malone Jul 07 '19 at 01:18
-
@TimMalone Is your comment posted in the right place? I don't see how this relates to my answer. In step 1, the user is not identified so it's immaterial which account will be looked up in step 3. – Douglas Held Jul 07 '19 at 09:47
When the user logs in, grab their password in the client-side code.
Hash the password and salt with a secure, long-running hash algorithm. You need something that takes sufficiently long to run that it makes brute-forcing the password given the hash impractical, even when the attacker has access to a lot of computing power.
Have the client-side code hash the user's password with the same salt and algorithm when the user attempts to log in next. If the hashes match, your client-side code has some evidence that the user has entered the correct password. If the hashes don't match, the user might have entered the wrong password. (The reason you can't be sure is because the user may have changed their password from another machine.)
But, as MechMK1 notes above, the fact that you even want to try and validate the password client-side suggests that you may be making really poor security design choices, such as doing something client-side that you expect a user without the correct password not to be able to interfere with. Plus, this whole scheme comes at the cost of leaving a hashed copy of the user's password laying around on their computer, waiting to be stolen and brute-forced if it turns out you picked the wrong hashing scheme.
A local password validation scheme could safely be used to prevent the user from wasting time or login attempts sending typo-d passwords to the server. However, since the user controls their local machine, they could easily bypass it by, among other things, deleting the cached password hash, or by replacing the hash that is stored with their own pre-prepared hash for a known password, or even by bypassing the code that checks the hash entirely. So this is not a viable way for the client-side code to actually authenticate the user. The client-side code is ultimately under the user's control and necessarily operates as the user's agent, not yours.
A related and potentially good design option would be encrypting locally cached user data with a key based on the user's password (with a sufficiently slow derivation algorithm to deter brute-force attacks). In this case, the client-side code doesn't need to try and authenticate the user; the encryption itself ensures that only the user has access to their local data.
- 305
- 1
- 4
-
7The bottom line is that if you are executing code client side, It's very easy get that code. Once I have your code, I can figure out what you want to see and give it to you. – Mohair Jun 21 '19 at 17:45
-
The listed steps are not scalable. Suppose I have 30,000 users, do I drop my entire hashed database into the client every time it loads? – Nathan Goings Jun 22 '19 at 21:32
-
@NathanGoings Only if you need to support validating logins locally for any user on any device. What I specced out is designed for one or a few users per device. But if you actually do need 30,000 different users to be able to be authenticated locally, I don't think there's really a more scalable way to do it than having a local copy of a hashed password for each. Do you have a better method? – interfect Jun 24 '19 at 17:49