1

I am trying to ascertain the best way to keep someone logged into my website after I have verified the log in is correct.

I tried to have a look at "Keep Me Logged In" - the best approach where the most upvoted answer said that I should generate a token and then store this token in the database! Surely that is wholly unsecure because all it takes is a database hack and cookie editing to get into someone elses account?

Could someone please provide me the most currently up to date secure way of doing this? Thanks.

Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
kabeersvohra
  • 1,049
  • 1
  • 14
  • 31
  • the most secure way is to not have a website or login at all... can't hack what ain't there... – Marc B May 29 '15 at 17:27
  • The accepted answer in the link you provided has it right. – Kyle May 29 '15 at 17:28
  • possible duplicate of [How to securely pass data from php forms to html](http://stackoverflow.com/questions/30532831/how-to-securely-pass-data-from-php-forms-to-html) – Kyle May 29 '15 at 17:29
  • but I dont see how this is secure. Surely if someone hacks the database and retrieves the key they can simply edit the cookie and they are in. This is no more secure than storing plain text passwords in the database? – kabeersvohra May 29 '15 at 17:31
  • 1
    If someone hacks your database, then they have no need to log in to your website as someone else. They will have access to all of your information, the website its just an interface to read/write to the database. – Blake A. Nichols May 29 '15 at 17:36
  • sorry I posted the wrong link – kabeersvohra May 29 '15 at 17:37
  • Ok so storing the cookie data in the database is secure then? – kabeersvohra May 29 '15 at 17:40
  • possible duplicate of ["Keep Me Logged In" - the best approach](http://stackoverflow.com/questions/1354999/keep-me-logged-in-the-best-approach) – vakio May 29 '15 at 17:42
  • I think that you could hash the token with `password_hash` and then set the cookie to the salt used for the hash concatenated with the original token. This would prevent the clear-text discovery problem that you mention. I don't want to suggest this as an answer as I'm loathe to create new security procedures for a production environment even though I think this one is sound. – Neil Smithline May 29 '15 at 17:56
  • @NeilSmithline Actually `password_hash` is overkill here, if your token is a high-entropy source (e.g. 32 bytes from /dev/urandom, encoded with base64). `hash('sha256', $token)` is good. (See answer below.) – Scott Arciszewski May 29 '15 at 18:09
  • Yeah @ScottArciszewski - I guess that you needn't worry about precalculation with a strong secret so you can remove the salt. And you don't need to worry about brute-force with a strong secret so you can remove the iterations. – Neil Smithline May 29 '15 at 18:17

2 Answers2

4

We recently posted a blog about secure authentication with long-term persistence (a.k.a "Remember Me"), but the largest difference between this blog post and ircmaxell's answer to "Keep Me Logged In" - the best approach is a separation of the lookup (which is not constant-time) and the validation (which is constant-time).

In the strategy we outlined in our blog post, you aren't storing tokens in the database, you're storing an SHA-256 hash of a token. If an attacker leaks these values, he has to crack SHA-256 hashes of strong random tokens. They're better off just launching a reverse shell that lets them authenticate as any user (or proceed to take over the entire machine with a local kernel exploit).

Logging In (Simple and Basic)

  • Use bcrypt. Specifically password_verify(). Don't generate your own salts.

    If you want to go the extra mile, consider this bcrypt + AES library to encrypt the password hashes (which is mostly helpful if you have your database and webserver on separate hardware, since compromising the database won't give them the encryption key).

  • Rate-limit failed attempts. For example: after 5 failures per IP or username, require a CAPTCHA.

Long-Term Persistence ("Remember Me")

When logging in:

  1. Generate a secure random token.
  2. Generate a secure random identifier.
  3. Store the identifier and the token in a rememberme cookie.
  4. Store a SHA256 hash of the token in the database.
  5. When a user lands on the page, if they have a rememberme cookie, grab the identifier and do a database search.
  6. If there is an authentication token for this identifier, grab the SHA256 hash.
  7. Compare the hash of the token provided by the cookie with the SHA256 hash in the database using hash_equals().
  8. If it succeeds, set a session variable to that user's ID. Generate a new token. If it fails, delete the entry from the database.

Advantages of This Strategy

  • If a successful SQL injection attack leads to the tokens being leaked, all an attacker has is the SHA256 hash of various tokens. This is not helpful for compromising accounts.
  • Database searches are not constant-time. This is why the identifier is separated from the token.
  • Sequential identifiers leak the activity level of your application, which many businesses wish to keep confidential. The random identifier obfuscates this detail.

Disadvantages

  • (Future StackOverflow authors should feel free to populate this list if any are discovered.)
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206
  • Thank you very much! I am a fan of this method to provide an extra layer of security over these tokens but I guess there is no alternative to storing the tokens in the database. I wish that SO did not close this question as further input would be useful for myself and others. – kabeersvohra May 29 '15 at 19:30
  • You can bug the moderators in [room 11](https://chat.stackoverflow.com/rooms/11/php) if you wish to dispute it being closed. – Scott Arciszewski May 29 '15 at 19:31
  • thanks I will give it a go – kabeersvohra May 29 '15 at 19:35
1

Storing the generated token in the database in clear-text is clearly a risk but one that is generally considered acceptable. One mitigation technique is to require the user to re-authenticate prior to performing sensitive operations. For example, Amazon seems to require me to enter my password before I modify my account details unless I've recently entered my password.

Neil Smithline
  • 1,526
  • 9
  • 21
  • Yeah I was reluctant myself to do such a thing but I guess with some more hashing it would be acceptable to do this. At the end of the day if they have access to my database it is game over anyways :D – kabeersvohra May 29 '15 at 19:28
  • It does become just a matter of degree once the database is compromised. I'm glad you chose @ScottArciszewski's answer. He has a good understanding of the needed hashing that must be done. – Neil Smithline May 29 '15 at 19:31
  • yes I agree, thinking about it now hashing it will simply reveal another set of randomised strings so its pointless if it is random to begin with – kabeersvohra May 29 '15 at 19:40