Search code examples
phpsecuritycsrfcsrf-protection

Md5 unique id good enough for hidden form tokens?


I'm using md5(uniqid()) to generate a unique hash for my token hidden input on my forms (like Log in, Sign up, Settings, etc) for my File Sharing and Hosting service and for the user session, so I can compare those two after the form is submitted.

But I'm wondering if md5(uniqid()) is good enough after I've read that md5 has lots of security flaws.

Are there better or more secure ways of generating tokens for my forms?

Output example

<input type="hidden" name="token" value="4c1dd84d3458964ee6d59c728dc70160">

Solution

  • This token should just be an unpredictable code. The best you can do to get such an unpredictable code with a deterministic computer, is to generate a really random number.

    When you use the MD5 function with your uniqid, it does not add any randomness/unpredictability to your token, you (mis)use it as an encoder. The same goal you get with using the bin2hex() function, that's what MD5 does by default after calculating the binary hash. That said, the MD5 function is not unsafe here but has no advantage neither.

    The more important point is, that the function uniqid() is not unpredictable, it is based on the current timestamp. This is the unsafe part in your code. To get an unpredictable number you can use the function mcrypt_create_iv() which reads from the random source of the operating system.

    I would recommend to let PHP create the session token for you, with the session_start() function. If you really have reasons not to use a normal PHP session, then use mcrypt_create_iv() together with an encoding function like bin2hex() or base64_encode().

    EDIT:

    From your comments i see that this token is not used to maintain the session, instead to mitigate csrf. In this case of course the session_start function won't help (the session id should not be used as csrf token), but creating an unpredictable token is still important. This is an example of how this can be done:

    /**
     * Generates a random string of a given length, using the random source of
     * the operating system. The string contains only characters of this
     * alphabet: +/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
     * @param int $length Number of characters the string should have.
     * @return string A random base64 encoded string.
     */
    protected static function generateRandomBase64String($length)
    {
      if (!defined('MCRYPT_DEV_URANDOM')) throw new Exception('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).');
    
      // Generate random bytes, using the operating system's random source.
      // Since PHP 5.3 this also uses the random source on a Windows server.
      // Unlike /dev/random, the /dev/urandom does not block the server, if
      // there is not enough entropy available.
      $binaryLength = (int)($length * 3 / 4 + 1);
      $randomBinaryString = mcrypt_create_iv($binaryLength, MCRYPT_DEV_URANDOM);
      $randomBase64String = base64_encode($randomBinaryString);
      return substr($randomBase64String, 0, $length);
    }