Generating Random Tokens (in Python)
Posted Thursday, January 9, 2014, at 11:30PM
_Update 1⁄13: 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:
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.