I would prefer urandom over secrets.token_hex
, as it samples from a richer character set and hence needs a smaller length to achieve the same entropy.
os.urandom
, which reads from urandom, is considered secure (see the relevant answer in a question if urandom is secure). You can then read as much as you like from urandom and produce a random alphanummeric as follows:
import math
import os
def random_alphanumeric(str_len: int) -> str:
rand_len = 3 * (math.ceil(str_len / 3) + 1)
return base64.b64encode(os.urandom(rand_len), altchars=b'aA').decode('ascii')[:str_len]
NOTE: The above function is not secure. Since you need a "very quick way to generate an alphanumeric", this function sacrifices performance over security, since the frequencies of a
and A
(or whatever characters you choose to replace +
and /
with) will be increased compared to what urandom would give you otherwise.
If you put randomness above performance, you could do something like:
def secure_random_alphanumeric(str_len: int) -> str:
ret = ''
while len(ret) < str_len:
rand_len = 3 * (math.ceil((str_len - len(ret)) / 3) + 2)
ret += base64.b64encode(os.urandom(rand_len)).decode('ascii').replace('+', '').replace('/', '').replace('=', '')
return ret[:str_len]
Note that chaining replace
turns out to be faster than sequntially calling it, as per this answer.
Also, in the above, +1 is replaced by +2 when determining rand_len
to reduce the number of iterations needed to achieve the requested length. You could even replace by +3 or more to reduce even more the possibility for an iteration, but then you would loose in performance at the chained replace
calls.