0

I am creating a PHP login system. User will receive email with a onetime link to the website where the one-time link is gonna be checked and token provided as a cookie/session. My question is how to split the token and/or onetime link to prevent timing attacks.

My sessions table:

- uid (AI PK)
- datecreated (when welcome email sent with one-time link)
- datevalidated (once one-time link is confirmed and token loaded to the user as cookie/session)
- email (email to which the welcome email has to be sent)
- onetimelink (https://example.com/login/$onetimelink - sent via email)
- token (token which authenticates users for up to a week)

Tokens will be generated with the following code and saved to the db:

$onetimelink = bin2hex(random_bytes(15));
$token = bin2hex(random_bytes(15));
O. Jones
  • 103,626
  • 17
  • 118
  • 172
stefan999
  • 51
  • 5
  • Please [edit] your question to tell us how you generate your tokens. And, keep in mind that php's `password_verify()` function is designed to resist timing attacks. – O. Jones Nov 23 '20 at 14:16
  • why not use JWT and set it to be valid for 1 week? btw the one-time verify token which is sent via email has nothing to do with the auth, it should simply be used to verify the email so could ba a HMAC of email and a serverside secret, then do `/login/?email=` then re-hmac the email and verify its the same, then issue a JWT which handles the actual auth.. there shouldn't be "random" values used for auth, the point would be to sign things – Lawrence Cherone Nov 23 '20 at 14:26
  • There's a great [post here about rolling your own security](https://security.stackexchange.com/q/18197/86698). I applaud you for asking here about that specific attack, that's great! But make sure that you cover the others, too. You might want to even post your question over there, because that people on that site are very helpful for security-related questions. – Chris Haas Nov 23 '20 at 14:26
  • I am not familiar with JWT and how to do a simplest implementation in PHP. One-time verify token is there just in case someone tries to retrieve tokens via old emails. – stefan999 Nov 23 '20 at 14:39
  • there is libs for php which you can use, but its essentially done like: https://3v4l.org/7iuYl (don't use it your need to validate the date this wont), payload and header its json encoded and then signed, then each is base64 encoded and concatenated with a dot. Your comment about *One-time verify token is there just in case someone tries to retrieve tokens via old emails.* that's in the implementation of your code. just unset the one-time code from the db. – Lawrence Cherone Nov 23 '20 at 15:09

1 Answers1

2

Here's how I would do this.

When generating the email

  1. Create a hard-to-guess random text string token using a technique like this one. PHP random string generator. 20 characters at five bits/character give you 100 bits of randomness in the token. That should be more than enough.

  2. Use php password_hash() to handle that token as if it were a password.

  3. Store the output from password_hash() in your SQL table. DO NOT store the actual token in your table.

  4. Put the token in the URL of your email. (&token=zSBXsEkhNX6S8h5fjFbB for example)

When verifying the token when the user presents the URL from the email

  1. Read the hashed token from your SQL table.

  2. Use password_verify() (which is designed to be safe from timing and other cybercreep attacks) to validate the token that came in on the URL. If password_verify() fails reject the request with a very generic message like "sorry, link invalid", not "your token was wrong."

  3. When the token is verified, UPDATE or DELETE the row in the table so the hashed token is no longer present.

When maintaining your table

DELETE or UPDATE rows containing stale (too-old) hashed tokens. Don't leave the hashes for stale tokens floating around in your system.

If you do things this way you use tried-and-true cryptographic modules. That's safer than creating your own. Unless you're Bruce Schneier or Whit Diffie, that work is best left to experts.

This kind of token is called a

O. Jones
  • 103,626
  • 17
  • 118
  • 172
  • Thanks a lot... So I will try this $onetimelink = password_hash(bin2hex(random_bytes(30));.. Both for onetimetoken and token... And how do you propose to create a permanent login cookie/session with the retrieved token from the database? – stefan999 Nov 23 '20 at 15:00
  • Your proposal is not what I suggested. Your "one-time link" is your random token, not its hash. You store the hash, not the token, in your table. And, as far as php sessions go, you should probably use php's [built-in session management](https://www.php.net/manual/en/features.session.security.management.php). It too is resistant to cybercreep attack.. – O. Jones Nov 23 '20 at 15:23
  • But then anyone with access to the email can just see the token. With onetimetoken you only have the link but cannot store the actual token as that one is provided on the website only once and stored in the cookie. Afterwards the onetimelink is invalidated but not the token - this one still valid for a week. – stefan999 Nov 23 '20 at 15:33
  • And which hash or token then use for session/cookie? I guess it should be stored in the DB or? – stefan999 Nov 23 '20 at 19:04
  • I am also not sure how to do a select on the token in the database, since it's hashed there and not in the link? – stefan999 Nov 25 '20 at 16:51