If we used microtime() an attacker could only guess to within 1 second so they would have to brute force thousands of possible tokens.. but the token can expire after a single failed attempt.

With mt_rand() an attacker would need to reset 700 passwords to get enough information to piece together the random state, but that's assuming each call is consecutive.. other people making use of the site would make trigger mt_rand calls that the attacker is not aware of.

So it seems in both situations the attacker is thwarted. Of course it's better to be safe than sorry so it's better to use cryptographic random numbers but I'm wondering if it's an actual vulnerability not to.

Please search the forum, you can use random time plus the random 128 byte random initialization vector, and then you encrypt it with AES and this way it's OK. For AES you need a key which you can store on the server.
–
Andrew SmithAug 2 '12 at 17:18

@AndrewSmith, I do search the forum
–
user11101Aug 2 '12 at 17:22

Search for the posts about encryption and random iv - only encrypted data is safe, the rest is easy to crack with tools and methods.
–
Andrew SmithAug 2 '12 at 17:38

2

@AndrewSmith I'm sure the OP knows that these function are not a good choice. But it's certainly interesting to find out how bad they really are.
–
CodesInChaosAug 2 '12 at 21:36

Such sharp results in this paper, exactly what I wanted to know . Thanks a lot!
–
user11101Aug 3 '12 at 4:23

I am not sure about this secure token generation, particularly 64bit urandom based key. 256bit is used for purpose, and for average website you should use 128 bit keys today at absolute minimum assuming strong PRNG.
–
Andrew SmithAug 3 '12 at 11:04

You describe using microtime() and say one incorrect attempt invalidates the token. First, an attacker tries out doing one reset from an account you control to see how the token is generated. Maybe they find out it looks like an encoding of a unixtime set to some particular time zone. The attacker tries estimating the delay of the system (network time; CPU time). Yes, any particular attempt is likely to fail. But if your attacker with their botnet tries ~10 attempts a minute it would take about a day (if they can estimate the time to ~0.1s before you'd break in) or a week (~1 s accuracy).

Sure you could possibly lock the password reset mechanism account after too many incorrect attempts (but how would it be unlocked?), could this frustrate legitimate users? Oh you put a captcha on the page? Well know the attacker has to use amazon mechanical turk to defeat the captcha at a cost of ~$2 per thousand captchas (so it costs ~$20 to break into an account with a captcha). But again, if you can find existing a list login names easily (say at the account creation page if it informs you that a username is already in use), they don't have to attack any specific account. E.g., they change the account they attack nearly every time; possibly (with botnet) change the IP nearly everytime, etc.

In summary, its pretty trivial to implement this in a bulletproof secure manner, so why would you ever want to do it insecurely, where you expect any attacker off the street has a 1 in 100000 of getting in on any particular attempt? Versus getting some random 128-bit string where they'd have a 1 in 340 000 000 000 000 000 000 000 000 000 000 000 000 chance of randomly guessing it.

No, mt_rand() and microtime() are absolutely not safe at all. These are both 32bit numbers. A decent key is fully random (not time) and at least 128bit in size. Also mt_rand() is not cryptographically safe so it doesnt produce really random numbers which would be usable for this task.

You need to get 128bit key from /dev/urandom, which is better, it is 16 or 32 characters, and then you can hash it with salt, or encrypt with AES using another 128bit symmetric key, which you can obtain from /dev/random, which is slower, but the entropy is higher.