nette/nette — pull request #882 by juzna (2012-11-30 21:39:46+00:00)
Title: Security hole: String::random() entropy can be easily decreased to zero

53685ee9 claimed to use more entropy which isn't necessarily true and it is actually easy for an attacker to recover generated string.

This [presentation](http://www.slideshare.net/DefconRussia/reutov-yunusov-nagibin-random-numbers-take-ii) describes how poor entropy is in PHP and how can be easily reduced to zero. Nette tries to fix it and add more entropy by itself, which doesn't work at all though :)

## Possible hack
Attacker can use "I forgot my password" on a web to reset your password, which he can then obtain and login to your account (my PoC works within seconds).

I provide an [example](http://forum.nette.org/cs/12960-bezpecnostni-dira-v-punbb) on non-nette app. I won't publish a PoC on nette based app now for security reasons.

## Background
PHP has basically 3 random generators, each having its own status and seed. These are:
- *rand*, seeded by *srand*
- *mt_rand*, seeded by *mt_srand*
- lcg (Linear Congruential Generator) with *lcg_value*, seeded only by internal func lcg_seed

They all are seeded when first used, but they all use the **same inputs of entropy**: several microtimes & process id.

Status and seeds are global for a php process, i.e. they don't change between two following requests (if those are sent properly, see the presentation for details).

From one known random token it is possible to recover parameters used for seeds, thus also seeds themselves and also any following random tokens generated.

## Current state
`Strings::random()` uses these techniques to ensure randomness:

- *str_shuffle*, which uses *rand* and thus can be recovered (when entropy is decreased to zero)
- *lcg_value* - also no entropy
- *microtime(TRUE)*
- time in seconds is naturally known to the attacker
- microseconds part is actually the only proper source of entropy, but it is effectively stripped by the modulo operator in following lines

**Summary**: `Strings::random()` uses entropy provided by php (which it also did before 53685ee9). Additionally it changes output in the case when `$rand + $rand2 > 1`, which adds 1 bit of entropy.

Thus, that commit with message `String::random() uses more entropy` actually added one whole bit of entropy ;)

## Solution
- depend on time in seconds, useful in second and any further calls (was there already)
- each character depends on microseconds and a random value from lcg (added now)