The accepted answer seems to be providing snake oil alongside good advice.
For the generation of CSRF tokens specifically, there are only a few requirements:
- unpredictable
- some resistance to brute force
- uses printable characters that are compatible with web technology
All you need to do to achieve this is generate enough cryptographically secure randomness to prevent a modest brute force attempt (compared to, say, offline password cracking) and then base64 encode it.
You can see what is used widely by the Clojure community https://github.com/ring-clojure/ring-anti-forgery/blob/master/src/ring/middleware/anti_forgery.clj#L11 as part of the anti-forgery middleware (prevents CSRF).
(defn- new-token [] (random/base64 60))
The key is that the random/base64
function being called here uses the cryptographically secure Java SecureRandom
class under the hood. The 60
refers to 60 bytes of random data to be base64 encoded.
That's it.
You can achieve the same outcome by sourcing random data from /dev/urandom
.
Here are problems with the other suggestions for the token generation algorithm:
- Usage of
mt_rand
- don't use this at all, it's not fit for purpose here and is exactly what the linked article in the accepted answer warns against (but then the accepted answer goes on to use mt_rand
in code examples...)
- Usage of
uniq_id
- does not create unpredictable or random strings, as per the PHP docs
- Using a hash function - does reduce the character set, but doesn't add any security. Base64 encoding is clearer about the purpose and doesn't imply security functionality that simply isn't there
- Using a secret key - In other contexts secret keys are critical, in this context it simply provides you with something you have to keep secret (not an easy thing to do), adding complexity and difficulty with no extra security benefit
- adding client IP/port/etc. - Doesn't make the token more secure than crypto-random data, but does make the algorithm more complicated and less portable
- Usage of microtime - Doesn't add any extra security, the current time is predictable enough to be brute forced. If we consider
mt_rand
insecure then surely a timestamp is too.
Essentially, anything you do that adds complexity without meeting the fundamental requirements just makes the algorithm:
- Less maintainable
- Harder to understand
- More likely to have a critical bug
- Less secure