0

Register:

  1. Both text boxes of the user inputted password are compared, if they match:
  2. Add the user's email to the [users] table in the database, to have a user ID to later record everything against.
  3. Create salt. Anything random enough, and complex enough will suffice. UserName+current dateTime - MD5 hashed. GUID, UserName + Random number hashed, etc.
  4. Record the salt in a Salt table in the DB alongside the user ID and name.
  5. Add together the user Password and salt.
  6. Hash that using Encryption1
  7. Hash that using Encryption2
  8. Record that, alongside the user ID and their details (if any) they provided in the registration form.
  9. Optional email verification sent out, to make sure they are using a legit email.

Login.

  1. Get the salt using the user inputted username.
  2. If salt is returned do Steps 5-7 from the Register (pwd+salt, hash, hash)
  3. When we retrieved the salt, we also got a user ID, compare the outcome of Step 2 and the ID against the records in the database.
  4. If there's a match then the user used their email and password and are allowed to login.
  5. Create session variable and allow them to proceed.

A benefit I believe all this carries is, even if my entire database is accessed in a raw unprotected form, the password is a 50 character long string hashed twice using different encryption methods.

closed as too broad by Anders, Steve, Xiong Chiamiov, Xander, Tobi Nary Nov 3 '17 at 11:35

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • Couple of issues regarding your edit. 5: I'm not sure, but based on your pseudocode it looks to me like you're using a general purpose hash instead of a password hash. 7: Why use an extra database call? You should already have the hash from when you retrieved the user row to get the salt. – AndrolGenhald Nov 1 '17 at 13:54
  • @AndrolGenhald I'm using different SHA algorithms both times (not SHA1) When I get the salt I only get the salt, no hash is stored in that table where the salt is stored. Good answer link though, I'll have a read and update my code accordingly. – Иво Недев Nov 1 '17 at 14:22
  • 1
    @ИвоНедев Do not edit your question to respond to comments or Answers. Just make multiple comments if you need more space? – schroeder Nov 1 '17 at 22:22
2

Following your lead, I'm just going to list my thoughts:

  1. Make sure the email address isn't already in use
  2. To be clear, the MD5 hashing algorithm doesn't add randomness to anything. As a result, it isn't really doing a whole lot for you here, other than making it not-immediately-obvious where your salt came from. I think it would be better to use a CSPRNG to generate your salt than basing it off of user data and running it through MD5.
  3. This is an application nitpick, but it sounds like you are creating the user record (step 2) and then coming back and storing the salt (step 4) and then storing the hashed password (step 8). There is no reason you can't just do all of that in one insert operation. You'll get better performance, but most importantly, your code will be simpler and easier to maintain.
  4. To be clear, you don't want to use an encryption method to hash your passwords. Hashing and encryption are two separate concepts with different preferred algorithms. Make sure you are hashing: not encrypting. From what you have written I think that you are on the right page, and are simply using the wrong terminology. It's an easy mistake to make, but the difference is critical enough that it is worth me pointing this out.
  5. There is no good reason to use two separate encryption algorithms. The only reason to do such a thing is to make it so that people can't try to brute force your passwords if they find your database. However, it is a security measure that relies on your source code being kept secret. Sometimes they both get leaked though. This step isn't useless, but it also isn't as useful as you might think, and it complicates your code. If you want a protection like this I would just include a pepper and stick to a single pass of a secure password hashing algorithm.
  6. On login, make sure you only ever return a single, non-descriptive error message to the user "Invalid username/password combination"
  7. Don't do a simple comparison of the hashed password of the user to what comes out of the database (i.e. $hashed_input == $user_hash). There are some subtle ways in which such a straight-forward comparison can be used by a malicious attacker to help crack passwords. Some languages have helper methods to facility password hash comparisons. Use one or find one.

There can be a large number of subtle ways for security bugs to creep in to the login system of an application, which can easily be missed in an overview like this. Considering that it is the first line of defense of your application, it is important to get user authentication right. As a result, your best bet is to use a well-tested and well-supported authentication module that already exists, rather than building your own. If you do decide to stick with writing your own, then you should at least post your code on codereview.stackexchange.net afterward.

  • @ConorMancone Regarding point 7, is it actually important to avoid timing attacks against password hashes? A constant time comparison wouldn't hurt anything and there's no reason not to, but without going into the math my intuition is that a timing attack against a password hash would be at least an order of magnitude more difficult that attacking the hash directly. – AndrolGenhald Nov 1 '17 at 14:03
  • 1
    @AndrolGenhald timing attacks are a big part of what you're referring to. From that perspective, you're right: a timing attack is likely unrealistic, especially against a web application where network latency and variability dominates: I'm sure its impossible. However, there can be other concerns. For languages with loose comparison checks (PHP for instance), you can end up with exploits in the (rare) worst case scenario. As a result, my general policy here is to stick with a dedicated hash comparison function, if available. – Conor Mancone Nov 1 '17 at 14:24
  • @AndrolGenhald Related: security.stackexchange.com/questions/9192/… – Conor Mancone Nov 1 '17 at 14:24
2

A few notes, if not a full answer:

  1. DO NOT use a simple hash like SHA-x for your password hash(es). YOU MUST use a slow hash designed for password storage.
  2. If you properly use a password hash, your double-hash doesn't really add much to the security. In fact you'd probably be better served just increasing the number of rounds of your password hash instead.
  3. If email address is required for using your system, or if email address is used for password resets, then I would make the email verification required instead of optional. Otherwise malicious users could sign up other people (or prevent other people from creating an account), and legitimate users might typo their email address causing someone else to be able to gain control of their account by accident.

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