15

I've heard people say again and again that you shouldn't implement your own security algorithm, and I'd like to ask you if I'm breaking this rule.

I'm creating a traditional client-server application with a twist: I want to encrypt all data on the client so that even an attacker who gains complete control over my server will be unable to access the users' data. That is, I want end-to-end encryption for my client-server application.

The client encrypts the user's data with a cryptographic key derived from the user's password, so it's important that the server not be able to figure out the password because that would allow the server to decrypt the user's data. The client therefore uses Secure Remote Password protocol to avoid sending the password to the server. So far so good.

Now here's where my design gets a little unorthodox. The problem is that users in general are very bad at creating passwords - usually picking a password from the top 10,000 passwords list - which would make it pretty easy for an attacker who has access to the server to decrypt the user's data. Therefore I plan to generate a random set of characters and append them to the User ID field, so the user would need to log in with something like these credentials:

User ID: John-CPE4E38J
Password: snoopy

But before processing these credentials the login code moves the random characters to the password so the underlying SRP authentication library sees this:

User ID: John
Password: snoopy-CPE4E38J

In addition, the client offers to remember the User ID (ie. John-CPE4E38J) so that most of the time the user only has to remember their password.

What do you think of this approach? Am I breaking the do-not-implement-your-own-security-algorithm rule? Or is this a good way to strengthen the security of my application?

  • OP, please, don't be a Dave! – Malavos Jan 30 '15 at 20:58
  • crackstation.net/hashing-security.htm is very educational. – Jonathan Jan 31 '15 at 0:11
  • Malavos - who's Dave? – Mark Jan 31 '15 at 11:20
  • Not a direct answer to your question (there are plenty of those already), but if this is a web app and your concern is the possibility of an attacker gaining full control over your system, having client-side encryption won't necessarily help. Unless you have a way for the client-side code to be digitally signed (and verified!) the attacker could easily modify the client-side code to reveal the necessary private information. – Dan Herbert Feb 5 '15 at 3:55
7

Your idea is sort of a hack. But if you are constrained by existing software, which requires the credentials to consist of only two fields, then it could be a suitable hack.

If you are writing the client code and not subject to arbitrary constraints, you may be better off using three separate fields in order to make a clear distinction between the purpose of each of the three strings.

Having authentication depend on both a secret stored on the client machine and a secret remembered by the user is a sound approach. This is one kind of two-factor authentication, because the user need both the machine on which the secret is stored as well as knowing the password.

One has to worry about keeping a backup of the secret stored on the machine. This is important because if you lose it, there will be no way to decrypt the data anymore, so effectively all their data is lost.

You should also make use of a key derivation function, which requires some CPU time on the client to derive an actual key from the secret data. If this takes 100ms of CPU time, it is hardly noticeable to the user, but needing 100ms of CPU time for each attempted password will slow down an attacker significantly. It is important that the CPU time is spent on the client rather than on the server, because if it is on the server, it becomes a DoS vector.

Encrypting all data directly with a key derived from the user's password means that a password change would require all data to be re-encrypted. On the other hand keeping a fixed key for encrypting the data and encrypting that key using the key derived from the user's password is more efficient, but has a subtle security vulnerability.

An attacker who got a copy of the data from the server might be able to brute force some users' passwords. If the attacker obtain another copy of the data from the server later, the users' data can still be decrypted, even if the user has chosen a stronger password in the meantime. This means data which was encrypted only after the user switched to a stronger password remains vulnerable to attacks against the old weaker password.

This problem can be solved by storing data in a tree structure where each node is encrypted with a key stored in the parent node, and the root is encrypted using a key derived from the secret data.

Also notice that the secret data you store on the client need to change, whenever the password changes. Otherwise you don't get the intended security benefit.

Assume a user initially had a weak password, and an attacker was able to brute force the secret which was stored on the client because of that weak password. If the user switches to a stronger password, but the stored secret remains unchanged, the attacker only has to brute force the new password and not the combination of password and stored secret.

29

Yes: you're re-writing security here. There are already several password derivation functions that are known to work, the most well-known one being PBKDF2. Use that instead of your custom scheme.

What you're trying to do is, in essence, salt the password (with a short salt) and store the salt value in the user name. It's not really much of an improvement.

Something else you might want to rethink: with your system, the encryption key is directly derived from the user's password. This means that not only can an attacker attempt to decrypt the user data by attempting to use known password lists (even if using a password-based key derivation function makes it harder) but you also effectively prevent the user from ever changing his password (otherwise, you need to re-encrypt all the user's data).

Without knowing more about your requirement and environment, it's hard to suggest the best way to proceed but, from what you describe, I'm sure that this can be greatly improved. I suggest, therefore, that you hire a specialist to help you find a proper solution in this case.

  • OK, but then where is the salt stored? If it is stored in the server database then an attacker who gains access to the server can probably decrypt the user's data by running through a list of the 10,000 most common passwords. By storing the salt with the user ID it makes such an attack far more difficult, doesn't it? – Mark Jan 30 '15 at 1:30
  • I should add that in my design, once the 'user-ID salt' is added to the password the cryptographic key is generated in the usual way - that is, by passing the password and a server-supplied salt to a key derivation function such as Scrypt or PBKDF2. So effectively there would be two salts - one stored on the server and another in the User ID field. – Mark Jan 30 '15 at 2:03
  • The salt is typically stored server-side, yes. And it is not supposed to be private data. Typically, you do not encrypt user data directly with the key derived from the password. Instead, you generate a random key, and encrypt that key with the derived one. The whole setup is more complex because you need to store that encrypted key but how you do that depends on your environment. That's why I suggest you get help from a professional: solving that problem is typically something that you have to do on a case-by-case basis and that can be rather delicate. – Stephane Jan 30 '15 at 8:08
  • Suggesting a key derivation function as alternative to the idea in the question is sort of malplaced. The two are orthogonal. The 8 random characters appended to the user id would slow down a brute force attack a billion times. The key derivation function would only slow down a brute force attack maybe a million times. But the two can be combined to slow down an attack a trillion times. – kasperd Jan 30 '15 at 9:09
  • 2
    @AaronLS In order to achieve the same with a salt, that salt has to be stored only on the client. Anything stored on the server would be available to the attacker in the threat scenario described in the question. The entire point of the idea described in the question is to increase the amount of entropy in secrets unknown to the attacker by storing a secret value on the client which will be needed in addition to the password. – kasperd Jan 30 '15 at 18:02
6

Your approach is very similar to the concept of salt in cryptography. Before talking about what is a salt, I assume that you, at least, are storing the hash of the users' passwords, and not the passwords in plain text. Otherwise, your solution is completely useless.

A salt is simply random data that is used when hashing a password, for example, for storage purposes. Your random string appended to the username is effectively a salt. So if you were simply storing the unsalted hashes of users' passwords, then, in a way, you are somewhat strengthening the security of your application, since at least the list of password hashes would not be vulnerable to dictionary attacks, in principle. But only in that case. Anyway, there are standard ways of protecting lists of password hashes, which do not involve disclosing the salt during the authentication.

However, if your intention was to make password guessing difficult, then it does not provide any additional security, since the random string is provided to the attacker during authentication.

  • 2
    There is one very important difference between a salt and the random string proposed in the question. A salt is generally assumed to be stored in clear on the server where an intruder might obtain a copy of it. However the random string proposed in the question would only be stored on the client, and thus not available to the attacker in the described threat scenario. – kasperd Jan 30 '15 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.