Generating Random Tokens (in Python)

_Update 113: After reading the comments and thinking about it some more, I think binascii.hexlify(os.urandom(n)) is the easiest way to generate random tokens, and random = random.SystemRandom(); ''.join(random.choice(alphabet) for _ in range(n)) is better when you need a string that contains only characters from a specific alphabet. Pyramid uses the former approach; Django uses the latter.


I’m working on a web site where I need to generate random CSRF tokens. After digging around a bit, I found os.urandom(<em>n</em>), which returns “a string of n random bytes suitable for cryptographic use.” Okay, that sounds good… except that it can include bytes that aren’t “web safe”.

So I needed a way to encode the output of urandom. I poked around some more and saw binascii.hexlify(<em>data</em>) being used for this purpose (in Pyramid). For some reason, though, I thought it would be “clever” to hash the output from urandom like so: hashlib.sha1(os.urandom(128)).hexdigest().

What I like about this is that no matter how many bytes you request from urandom (assuming more bytes means more entropy), you always end up with a 40 character string that’s safely encoded.

I’m not sure if this provides any real benefit though (in terms of increased security). Are there better ways to generate random tokens?

Another thought I had was to use bcrypt.gensalt() and use its output as is–it uses urandom to generate the initial salt, which is then hashed, and also returns a fixed number of bytes (29).

On a slightly related note, I recently needed to generate a new PIN. My first thought was to reuse a PIN I use elsewhere, but of course that’s a bad idea. My second thought was to use KeePassX to generate one. I happened to have a calculator sitting next to me (one with big buttons); I closed my eyes and banged on it a bit to generate the PIN.