I'm generating a token to be used when clicking on the link in a verification e-mail. I plan on using uniqid() but the output will be predictable allowing an attacker to bypass the confirmation e-mails. My solution is to hash it. But that's still not enough because if the hash I'm using is discovered then it will still be predictable. The solution is to salt it. That I'm not sure how to do because if I use a function to give variable salt (e.g. in pseudocode hash(uniqid()+time())) then isn't the uniqueness of the hash no longer guaranteed? Should I use a constant hash and that would be good enough (e.g. hash(uniqid()+asd741))

I think all answers miss an important point. It needs to be unique. What if openssl_random_pseudo_bytes() procduces the same number twice? Then one user wouldn't be able to activate his account. Is people's counter argument that it's unlikely for it to produce the same number twice? That's why I was considering uniqid() because it's output is unique.

I guess I could use both and append them together.

  • Well no hard feelings but they didn't answer the question. It's been too long to remove the down votes.
    – Celeritas
    Commented Aug 8, 2013 at 21:52
  • @Celeritas Quoting from Tom's answer: “making collisions so utterly improbable that you don't need to worry about them”. The token has to be generated in such a way that it won't collide with a value picked by the attacker, and the attacker may be using openssl_random_pseudo_bytes as well, to the token will not collide with another token that you generate (except with less-likely-than-a-meteorite-strike probability). Commented Aug 8, 2013 at 21:56
  • @Celeritas All of the answers have directly answered your question. The issue is with your own lack of knowledge. Honestly, there's nothing wrong with not knowing stuff, I certainly had no idea about crypto 6 months ago. openssl_random_pseudo_bytes gets its random bytes from sources that the OS has put a lot effort in generating. It gathers a lot of entropy to come up with them. So collisions are very unlikely, they're at least as unlikely as collisions from uniqid() if not even more.
    – Adi
    Commented Aug 8, 2013 at 21:58
  • @TildalWave That's obvious to someone who's been immersed in security or crypto for a while. Please consider that it is not obvious to everyone. The relationship between randomness and uniqueness is actually fairly subtle. Commented Aug 8, 2013 at 22:11
  • In practice using "truly random" bytes is a better way to ensure uniqueness (keeping in mind the birthday problem) than any other source of information. Other methods (like time or UUIDs) require more caution, increases the likelihood of repetitions, or needs more organization. And those methods are, of course, more predictable than random data. Commented Nov 14, 2018 at 17:45

4 Answers 4


You want unguessable randomness. Then, use unguessable randomness. Use openssl_random_pseudo_bytes() which will plug into the local cryptographically strong PRNG. Don't use rand() or mt_rand(), since these are predictable (mt_rand() is statistically good but it does not hold against sentient attackers). Don't compromise, use a good PRNG. Don't try to make something yourself by throwing in hash function and the like; only sorrow lies at the end of this path.

Generate 16 bytes, then encode them into a string if you need a string. bin2hex() encodes in hexadecimal; 16 bytes become 32 characters. base64_encode() encodes in Base64; 16 bytes become 24 characters (the last two of which being '=' signs).

16 bytes is 128 bits, that's the "safe value" making collisions so utterly improbable that you don't need to worry about them. Don't go below that unless you have a good reason (and, even then, don't go below 16 anyway).


Make sure you have OpenSSL support, and you'll never go wrong with this one-liner

$token = bin2hex(openssl_random_pseudo_bytes(16));
  • 1
    12 bytes are a bit low to my taste; better make it 16. Why stop at 96 bits when you can boast about "128-bit security" ? Powers of 2 are supreme.
    – Tom Leek
    Commented Aug 8, 2013 at 21:16
  • @TomLeek While I don't particularly disagree, I still feel that for this usage (short-lived confirmation emails) 96 bits is very decent. But hey, OP or others, knock yourselves out. Why stop at 128? Go for 256 or even 512. One thing I definitely agree on, 128-bit sure sounds better.
    – Adi
    Commented Aug 8, 2013 at 21:23
  • @Svetlana I feel unnaturally inclined toward upvoting this answer purely because of the profile picture.
    – tylerl
    Commented Aug 9, 2013 at 7:09
  • Security is about "guessing" the token, hence 96-bit security for a 96-bit token (and yes, that's good enough), but there is also a usability issue related to collisions, in case you use the token as an indexing key. With 96-bit tokens, first collisions will be encountered on average after 2^48 tokens -- and that is the number which is "a bit low to my taste". 128-bit keys, 128-bit tokens (e.g. UUID) are a nice "default" value so that's what I recommend for something as general as "you'll never go wrong".
    – Tom Leek
    Commented Aug 9, 2013 at 11:22

In PHP 7, you can just use random_bytes:

Generates an arbitrary length string of cryptographic random bytes that are suitable for cryptographic use, such as when generating salts, keys or initialization vectors.

So to generate a 16 byte hex token you would do:

$token = bin2hex(random_bytes(16));

Formally, there is no guarantee that all numbers will be unique. But a collission is so unlikely to happend that you'll be better off worrying about being hit by a meterorite.

  • This is the best solution now. Better than OpenSSL The best way to get uniqueness within the context of a single process is to increment a counter. (Atomically and without being reset even if power goes out.) The best way to get uniqueness globally is to take a sufficiently large sample from a TRNG. 16 bytes is definitely more than enough for confirmation emails. In other contexts 24-32 bytes is better. Commented Nov 14, 2018 at 17:25

This is the function I use to generate a random token in PHP :

function generateToken($length = 24) {
        if(function_exists('openssl_random_pseudo_bytes')) {
            $token = base64_encode(openssl_random_pseudo_bytes($length, $strong));
            if($strong == TRUE)
                return strtr(substr($token, 0, $length), '+/=', '-_,'); //base64 is about 33% longer, so we need to truncate the result

        //fallback to mt_rand if php < 5.3 or no openssl available
        $characters = '0123456789';
        $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+'; 
        $charactersLength = strlen($characters)-1;
        $token = '';

        //select some random characters
        for ($i = 0; $i < $length; $i++) {
            $token .= $characters[mt_rand(0, $charactersLength)];

        return $token;
  • please explain how it works
    – Celeritas
    Commented Aug 8, 2013 at 21:05
  • I am not sure what you want me to explain, this function generates a random token that can be embedded in an email. The token is then stored in a database.
    – null
    Commented Aug 8, 2013 at 21:10
  • 3
    Is mt_rand unpredictable? If it isn't, you shouldn't fall back to it, you should error out instead. Commented Aug 8, 2013 at 21:20
  • 1
    I am going to quote @TomLeek "mt_rand() is statistically good but it does not hold against sentient attackers". Anyway if you cannot do otherwise it still better than nothing.
    – null
    Commented Aug 8, 2013 at 22:27
  • This is not safe. Falling back on mt_rand will make your numbers predictable. That is not an acceptable solution to the problem.
    – Anders
    Commented Nov 14, 2018 at 15:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .