Question

I am not really sure where the shared secret takes place.

I want users to be authenticated for a distinct page when they login with an action link in their email. After verify the key gets deleted from the database.

A couple of questions

  1. is this what I need to send in the email and do I need to randomize the secret also
  2. do I store the hash and keep the secret in a constant
  3. compare the hash-received with the database and
    second compare the associated userid with the decoded userid

  4. what length does the db field need to be

    $hmac = echo hash_hmac('sha256', 'userid|timestamp|somedata', 'tfjtfkmg'); url?hash=$hmac

do I verify the email like with the example code from facebook, although facebook uses two hmacs, one for sig and one for payload and base64 it.

is this correct?

function parse_signed_request($signed_request) {
  list($encoded_sig, $payload) = explode('.', $signed_request, 2); 

  $secret = "appsecret"; // Use your app secret here

  // decode the data
  $sig = base64_url_decode($encoded_sig);
  $data = json_decode(base64_url_decode($payload), true);

  // confirm the signature
  $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
  if ($sig !== $expected_sig) {
    error_log('Bad Signed JSON signature!');
    return null;
  }

  return $data;
}

function base64_url_decode($input) {
  return base64_decode(strtr($input, '-_', '+/'));
}
Était-ce utile?

La solution

First of all; there is no such thing as a shared secret when generating a hash.

I quote

Hash functions are primarily used to generate fixed-length output data that acts as a shortened reference to the original data. This is useful when the original data is too cumbersome to use in its entirety.

And I quote again

The shared secret can be used for authentication (for instance when logging into a remote system) [...] or it can be fed to a key derivation function to produce one or more keys to use for encryption

So basically a hash algorithm has a lot of uses, and generating a protected link wasn't the original meaning, but a good use though!

And a shared secret (or public key) is (most commonly) used to generate an encrypted version of a file (or message) that can only be decrypted by the creator of the shared secret using his private secret (private key). Sorry, can't be too extensive, as there is way too much to tell about the subject :) if you want to know more, start with reading the sources of the quotes above.


Back to your question now. The first question you'll have to ask yourself when encrypting something is how bad does someone want the encrypted info (or wants to get in in your case). In encryption there is no such thing as 'unbreakable', but the best encryption algorithms take such a long time to crack that people don't even try.

In your case, just make sure it is pretty hard to 'guess' the hash (more precise; guess the parameters used to create it). You can, for example, use the time in milliseconds, a random salt, etc. But make sure every parameter you use can be retrieved later on, eg. by storing it in a database next to the generated hash itself. In example storing a users IP isn't useful here, as the user can request the login hash at his office, go back home, and click on the link there. Then suddenly the link wouldn't be valid anymore as his IP 'changed'.

Long story short, an answer on your questions: basically you'll have to send a string (hash) that can only be reproduced by yourself. For that you - and you only - need to know what kind of parameters where used for creating the hash. So every single parameter needs to be stored in the database or can be reproduced in another way. So yes, I would advise you the randomize the secret for each link, but remember to store it, eg. next to the hash itself in your database.

The hash you receive should be compared with the one in the database, preferably checking the expiry date/time as well (just store {generation time} + 3600 seconds in your database and compare it against the time the user trys to login your system. In/decrease the 3600 seconds to increase/decrease the time window in which the user can login using the link.

Your last question (compare the associated user id) isn't relevant, as you cannot decode a hash.

The length of a database field depends. For a sha2 you'll need 64 characters, for md5 32 characters, and there are others.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top