Is Python uuid.uuid4 strong enough for password reset links?
Asked Answered
L

2

21

This is the code I use to generate a password reset link for my app:

def create_unique_code():
    return str(uuid.uuid4())

Is that strong enough? I use a one or two day expiry time.

Lodmilla answered 6/1, 2017 at 12:11 Comment(1)
That's totally fine, uuid4 is cryptosafe on cPython at least. A nice way to avoid storing those unique codes in the database is to to encode the user_id in the code itself. A nice example is how Django has implemented it: docs.djangoproject.com/en/4.0/topics/signingUlrika
C
11

Yes, a UUID4 is fully random and long enough to rule out brute forcing or lucky guesses. So as long as whatever RNG uuid.uuid4() provides sufficiently good randomness you should be fine.

However, consider using e.g. a cryptographically signed token (the itsdangerous lib can take care of it) - not only can you specify an expiry time right when generating it, you also won't necessarily have to store anything about the token on your server.

Crescentia answered 6/1, 2017 at 12:19 Comment(1)
maybe also worth mentioning the secrets module new in python3.6.Ludwog
C
31

In CPython, yes. In other Python implementations, probably, but you might want to double-check that a cryptographically strong source of randomness is used to generate the UUID.


There are two factors you might care about when judging whether some way of generating secure random tokens - such as UUIDs - is "strong enough":

  1. Are there enough possible values for it not to be brute-forced?
  2. Is the source of randomness used cryptographically secure?

Since there are 2122 version 4 UUIDs (that's a little over 5 trillion trillion trillion), the answer to point 1 is definitely "yes", in this case. The space of all possible UUIDs ain't going to be brute forceable any time soon.

Point 2 is not currently answered by the official Python docs on uuid.uuid4(), which make no mention of security or whether the randomness source used is strong. Indeed, the entire documentation of uuid4() is just:

Generate a random UUID.

which clearly provides no security guarantees.

Nor is it addressed by the UUID specification, which does not mandate a cryptographically strong source of randomness be used in UUID generation and indeed explicitly contemplates the possibility of a "predictable random number source" being used to generate UUIDs in the Security Considerations section.

However, we can look at the implementation at https://github.com/python/cpython/blob/master/Lib/uuid.py:

def uuid4():
    """Generate a random UUID."""
    return UUID(bytes=os.urandom(16), version=4)

Since this uses os.urandom as its randomness source, it's secure. See the docs at https://docs.python.org/3/library/os.html#os.urandom which note that os.urandom returns:

a string of size random bytes suitable for cryptographic use.

Chavaree answered 29/11, 2018 at 15:29 Comment(5)
bit of a side note, but does that mean that os.urandom can block? AFAIK anything that guarantees suitability for cryptographic purposes has the possibility of blocking at some pointInductive
@Cruncher, check the link for urandom in the answer, it appears that it can block.Nostrum
Check the linux manpage: man urandom: "When a Linux system starts up without much operator interaction, the entropy pool may be in a fairly predictable state. This reduces the actual amount of noise in the entropy pool below the estimate." As the manpage suggests, you need some gymnastics before using /dev/urandom for it to be considered cryptographically secure.Telly
@MasoodKhaari Sure, but Python's os.urandom function does those gymnastics - see the linked docs.Chavaree
@MarkAmery That's true from python 3.6 onward: "Changed in version 3.6.0: On Linux, getrandom() is now used in blocking mode to increase the security", i.e., python doesn't fall back on reading /dev/urandom.Telly
C
11

Yes, a UUID4 is fully random and long enough to rule out brute forcing or lucky guesses. So as long as whatever RNG uuid.uuid4() provides sufficiently good randomness you should be fine.

However, consider using e.g. a cryptographically signed token (the itsdangerous lib can take care of it) - not only can you specify an expiry time right when generating it, you also won't necessarily have to store anything about the token on your server.

Crescentia answered 6/1, 2017 at 12:19 Comment(1)
maybe also worth mentioning the secrets module new in python3.6.Ludwog

© 2022 - 2024 — McMap. All rights reserved.