Skip to content

Use CSPRNG for random UUID node ID #135244

Closed

Description

@LamentXU123

Feature or enhancement

Proposal:

Issue for: #135226
(I forgot to open issue first, sry)
I'll do the benchmark of the performance impact when switching to secrets.getrandbits() for the clock sequence soon. I'll post here.
I totally agree with the previous discussion. Summary:

  • Maybe make it more clearer in the docs of UUIDv8(?) Its possible to predict UUIDv8 if hackers have collected enough UUIDs. BUT, UUIDv8 is highly customizable. It's pretty ez if someone wants to use CSPRNG in UUIDv8s by writing a wrapper outside the call(or using UUIDv4 instead lol).
  • Change _random_getnode() from PRNG to CSPRNG which randomly generates 48-bits note when python failed to get MAC address
def _random_getnode():
+	import secrets
+	return secrets.randbits(48) | (1 << 40)
-	import random
-	return random.getranbits(48) | (1 << 40)
  • Change the logic when generating clock_seq from PRNG to CSPRNG in both uuidv1() and uuidv6(). Well I think this is of great importance. Those two algorithm are time-based. The only random thing comes from clock_seq. Hackers can predict the next UUID if they got 624*32 random bits generated from the previous UUIDs (1427 UUIDs will be enough).
def uuid1(node=None, clock_seq=None):
    ...
    if clock_seq is None:
+       import secrets
+       clock_seq = secrets.randbits(14) # instead of stable storage
-		import random
-		clock_seq = random.getrandbits(14) # instead of stable storage
    ...

def uuid6(node=None, clock_seq=None):
    ...
+       import secrets
+       clock_seq = secrets.randbits(14) # instead of stable storage
-		import random
-		clock_seq = random.getrandbits(14) # instead of stable storage
    ...

note that nothing will be changed if the performance impact is unacceptable.

Has this already been discussed elsewhere?

I have already discussed this feature proposal on Discourse

Links to previous discussion of this feature:

#135226

Linked PRs

Note

3.13 needs a backport but we need to wait until the MAC address detection PR is merged (see #135244 (comment)), as otherwise, there is a small penalty at import time and upon first use of uuid1() and uuid6().

Activity

  1. LamentXU123 commented on Jun 8, 2025

    @LamentXU123
    ContributorAuthor

    Another CSPRNG( os.urandom() + cutoff + int.from_bytes()) is considered if its faster than secrets.randbits()

    I've run the brench mark wieh pyperf. It seems like os.urandom() might be faster.

        from os import urandom
        return int.from_bytes(urandom(2), 'big') & 0x3fff

    so to sum up, may we use os.urandom() instead of random.getrandbits() in clock_seq and notes generations, and make clearer security clarifications in doc of UUIDv8? @picnixz

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Jun 8, 2025
  3. picnixz commented on Jun 8, 2025

    @picnixz
    Member

    Hackers can predict the next UUID if they got 624*32 random bits generated from the previous UUIDs (1427 UUIDs will be enough).

    To be precise, I should have said that this is assuming that the UUIDs are generated sequentially and that the underlying MT-19937 state is not touched in between, so it's a bit more complex.

  4. picnixz commented on Jun 8, 2025

    @picnixz
    Member

    so to sum up, may we use os.urandom() instead of random.getrandbits() in clock_seq and notes generations

    I think I saw the benchmarks results somewhere. Can you post them in full as well? (I think you edited your message).

  5. changed the title [-]Get random in UUID more cryptography secure[/-] [+]Use CSPRNG for random UUID node ID & clock sequence #135226 [/+] on Jun 8, 2025
  6. changed the title [-]Use CSPRNG for random UUID node ID & clock sequence #135226 [/-] [+]Use CSPRNG for random UUID node ID & clock sequenc[/+] on Jun 8, 2025
  7. changed the title [-]Use CSPRNG for random UUID node ID & clock sequenc[/-] [+]Use CSPRNG for random UUID node ID & clock sequence[/+] on Jun 8, 2025
  8. added
    type-bugAn unexpected behavior, bug, or error
    3.13only security fixes
    3.14bugs and security fixes
    3.15bugs and security fixes
    and removed
    type-featureA feature request or enhancement
    on Jun 8, 2025
  9. picnixz commented on Jun 8, 2025

    @picnixz
    Member

    I'll treat this as a bugfix but not a security bug fix as it's not critical enough IMO.

  10. LamentXU123 commented on Jun 8, 2025

    @LamentXU123
    ContributorAuthor

    so to sum up, may we use os.urandom() instead of random.getrandbits() in clock_seq and notes generations

    I think I saw the benchmarks results somewhere. Can you post them in full as well? (I think you edited your message).

    os.urandom(): Mean +- std dev: 205 ns +- 6 ns
    random.getrandbits(): Mean +- std dev: 105 ns +- 5 ns
    secrets.randbits(): Mean +- std dev: 17.6 us +- 2.3 us

    pyperf on python 3.9

    benchmark_UUID.json

  11. 36 remaining items

  12. picnixz commented on Jun 15, 2025

    @picnixz
    Member

    Again, the attack surface is way too small and quite hard to exploit.

    But I think we could provide a more secure uuid8 func in case of some users use uuid8() with default attr, which is completely wrong, but possible I think, and we COULD avoid it by changing b to CSPRNG.

    This penalizes other users, which I don't want to. And it wouldn't really solve the issue. Assuming that the adversary is able to attack a UUIDv8 where a, b, and c are generated by the same PRNG (this is the case here), then even if 'b' is generated by a CSPRNG, they can ignore it. And then, they can predict the next 'a' and 'c' chunks, and can just bruteforce the 'b' chunk directly (12 bits is only 5k possibilities, and if they can spam 200 API sequential calls to get their password-reset token, we should assume they can also spam 5k sequential API calls).

    Once again, I will reject this feature.

  13. added 2 commits that reference this issue on Jun 19, 2025
  14. added 2 commits that reference this issue on Jul 12, 2025
  15. hugovk commented on Jul 22, 2025

    @hugovk
    Member

    3.14 RC1 note: this has been fixed for 3.14, the deferred-blocker label is for 3.13.

  16. added 2 commits that reference this issue on Aug 4, 2025
  17. added a commit that references this issue on Aug 5, 2025
  18. added a commit that references this issue on Aug 5, 2025
  19. added 2 commits that reference this issue on Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.13only security fixes3.14bugs and security fixes3.15bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions