Search code examples
phpsecuritytokenremember-melogin-control

What is the most secure method of logging someone into a website?


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.


Solution

  • 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.)