Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

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.

share|improve this question
    
Well no hard feelings but they didn't answer the question. It's been too long to remove the down votes. –  Celeritas Aug 8 '13 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). –  Gilles Aug 8 '13 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. –  Adnan Aug 8 '13 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. –  Gilles Aug 8 '13 at 22:11
add comment

3 Answers

up vote 7 down vote accepted

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

share|improve this answer
add comment

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

$token = bin2hex(openssl_random_pseudo_bytes(16));
share|improve this answer
    
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 Aug 8 '13 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. –  Adnan Aug 8 '13 at 21:23
    
@Svetlana I feel unnaturally inclined toward upvoting this answer purely because of the profile picture. –  tylerl Aug 9 '13 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 Aug 9 '13 at 11:22
add comment

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

<?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;
}
?>
share|improve this answer
    
please explain how it works –  Celeritas Aug 8 '13 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. –  Techbrunch Aug 8 '13 at 21:10
    
Is mt_rand unpredictable? If it isn't, you shouldn't fall back to it, you should error out instead. –  Gilles Aug 8 '13 at 21:20
    
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. –  Techbrunch Aug 8 '13 at 22:27
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.