2

I'm creating a login system for a website I'm building. Here's how I'm doing it currently.

  1. Use HTTPS throughout
  2. Encrypt passwords using PHP's password_hash() function.
  3. Generate a new token and delete the old one on every new login.
  4. Login user using a cookie, storing the users token
  5. Check cookie on each page load against a SHA1 hashed version of the token stored in the cookie.
  6. If the token is deleted from the database the user is logged out.

Is this secure? Is there anything else I should be doing?

  • Is the token a SHA1 hash or do you hash the token with a SHA1? – Yorick de Wid Sep 1 '16 at 14:10
  • The token in the database is a SHA1 hash of the token in the cookie. – Francis Sep 1 '16 at 14:14
  • how was the token generated? Or is it a JWT kind of token? – Yorick de Wid Sep 1 '16 at 14:20
  • It's a custom PHP function that generates a 64 character long string that can contain A-Za-z0-9. – Francis Sep 1 '16 at 14:24
1

Lets assume you are generating the token with enough entropy, and you do not use the default rand(). Lets also assume you run PHP 5.5.0 or above which supports bcrypt(), then kudos there.

It is completely unclear to me what the SHA1 is supposed to do, or protect against. It will not provide security or confidentially because anyone could hash the token with SHA1. To some extend it validates integrity, but for what? Storing the token in the database without any obfuscation does the exact same thing.

If your tokens are valid for a longer period of time, say longer than a few hours, I would suggest:

  • Use refresh tokens, so you can revoke tokens from clients much faster.
  • Sign the tokens upon creation and validate the signature on each request. This will confirm integrity and authenticity.

As a last tip, do never assume SSL/TLS is the holy grail, but use defense in depth.

  • I currently have 2 issues with this answer: 1. I would assume the tokens are stored in the DB as a hash so that if someone somehow got a hold of the hashes directly from the DB, they would still not necessarily be able to impersonate a user without the original token. (Though SHA1 may not be the best choice, but is certainly better than nothing.) 2. I disagree that SSL ensures trust at best. That's what an SSL cert is for. SSL is for keeping your data safe from prying eyes- but I'm sure you already knew that... – TTT Sep 1 '16 at 15:46
  • @TTT 2.) I misread, thx. 1.) I agree, for the situation that you sketch this is true, but it adds little to nothing to security, which is what I pointed out. If we're talking probability, you might as well assume the tmp/ got hacked and now all the php sessions are exposed. Heck, for all we know he stores the php sessions in a database as well. Hashing things is not the solution here, certainly not with a data integrity function. – Yorick de Wid Sep 1 '16 at 16:02
  • "It is recommended to log a salted-hash of the session ID instead of the session ID itself in order to allow for session-specific log correlation without exposing the session ID" - OWASP... also, third party may need a session ID for deduplication, providing the hash is a neat solution. I agree it's not usually a big deal but it doesn't hurt. – John Wu Sep 3 '16 at 0:51
0
  • Use HTTPS throughout

So far so good.

  • Encrypt passwords using PHP's password_hash() function.

Do you actually need to have your user's keep track of yet another user/password? Can't you use their internet's driver's license?

That said, assumming you can't or won't, make sure to use a good salt and a version of php that uses bcrypt (I believe it's default since 5.0).

  • Generate a new token and delete the old one on every new login.
  • Login user using a cookie, storing the users token
  • Check cookie on each page load against a SHA1 hashed version of the token stored in the cookie.
  • If the token is deleted from the database the user is logged out.

I assume you have an expiration time for the tokens and they are purged by a backend. What happens if the user is currently logged in and the backend detects the token expired?

There are several other things to consider.

CSRF

Be careful with CSRF on your login and logout forms. You don't want your users getting automatically logged out by third parties.

Actually, be careful with CSRF throughout the whole site.

Bruteforcing

Implement bruteforce measures on your login form and whatever backend it communicates to:

  • Increasing delay for failed login attempts
  • Mandatory captchas after a threshold of attempts
  • Temporary ban by IP address after a threshold of attempts

Monitor each of this measures, ensure they don't cause troubles for your users.

Password recovery mechanisms

How will your users recover forgotten passwords?

Ensure your mechanism is secure and doesn't leak personal information of your users.

Information leak

Make sure not to leak information on failed login attempts. An error message like this:

Invalid user

Gives valuable information to attackers (they can tell whether a username exists or not).

It's not enough to have a generic error message. This is much more nuanced than it seems at first: if you take 2 seconds to reply for bad passwords but only 100 milliseconds for bad usernames you're leaking information.

See CVE 2006-5229 for a good example of this last attack.

Ease of use

Security at the expense of usability comes at the expense of security

Use standard field names for username/password so your users can comfortably use a password manager if they choose to.

OWASP

For more details read the OWASP docs on authentication.

OWASP is a must for all web developers.

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.