The core distinction: identifier versus secret

A UUID gives you a value that is practically unique — something nothing else will collide with. That is useful. What it does not give you is a value that is unguessable by an authorised party in the way a password is. The two properties are often conflated, and confusing them leads to real vulnerabilities.

A password is a shared secret between you and an authentication system. Its security rests on the fact that only you know it, the system never stores it in plaintext, and guessing it is computationally expensive. A UUID v4 is 122 bits of randomness from a cryptographic source — yes, it is also practically unguessable — but it has no surrounding security properties. It is not hashed before storage, it appears in URLs, it travels in HTTP headers, and any system that processes a URL containing it likely logs it.

Use UUIDs as identifiers. Use proper secrets (from crypto.getRandomValues, crypto.randomBytes, or a purpose-built library) when you need a credential.

What UUID v4 actually guarantees

UUID v4 draws its 122 non-fixed bits from the platform’s CSPRNG — the same source backing crypto.randomUUID() in browsers and crypto.randomUUID() in Node.js. That means:

What v4 does not guarantee:

What UUID v7 leaks and when that matters

UUID v7 encodes a Unix millisecond timestamp in its high bits. This is intentional and useful for database indexes. It is a disclosure risk when the identifier is externally visible.

What anyone holding a v7 UUID can learn:

When this matters:

The standard recommendation: use v7 for internal primary keys where creation time is harmless or useful; use v4 for any identifier that leaves the system boundary.

Common mistakes

Using a UUID as an API key

An API key is a credential. It proves that the caller is authorised. A UUID in a header field can authenticate in the trivial sense of “this is a string I issued you,” but it has no revocation, no rotation, and no binding to an identity. If the key leaks — via a log file, a curl history entry, or an environment variable — there is no way to detect the leak or limit its blast radius.

Use a purpose-built API key format: an opaque 256-bit value from a CSPRNG, prefixed with a product identifier so it can be detected by secret-scanning tools (like GitHub’s push protection), stored as a hash on the server side.

Treating a UUID in a URL as access control

A UUID share link (for a document, a file, a payment page) is sometimes called a “capability URL” — knowing the URL is proof of access. This is legitimate for low-stakes sharing but it is not a substitute for authentication:

For anything sensitive, require authentication in addition to the URL token, and set a short expiry.

Generating UUIDs with Math.random()

Math.random() is a pseudo-random number generator seeded from a low-entropy source. It is deterministic and produces numbers that are not cryptographically random. Any UUID built from Math.random() outputs has far less than 122 bits of real entropy — in some JavaScript engine versions, the effective entropy is below 60 bits. Always use crypto.randomUUID() or crypto.getRandomValues().

Reusing UUIDs

An ID that gets recycled — for example, one that is deleted and then reinserted — carries the risk that authorisation decisions made on the old record apply to the new one, or that caches return stale data. Treat UUIDs as write-once and immutable.

Choosing between v4 and v7 from a security standpoint

ScenarioRecommendation
Database primary key, internal onlyv7 — sort order is useful, timestamp leaks nothing
URL passed to users or third partiesv4 — no timestamp, no timing leak
Idempotency key in an API callv4 — never re-used, never externally meaningful
Session IDNeither — use a dedicated session library with HMAC-backed tokens
Password reset / invite / unsubscribe linkv4 with server-side expiry — UUID is the token, expiry limits window
Event or log correlation IDv7 — creation time is exactly what you want to correlate by

Randomness source checklist

Before shipping UUID-based security-sensitive code, verify:

Frequently asked questions

Can I use a UUID as a secret token?

No. A UUID is an identifier, not a credential. The 122 bits of randomness in v4 make collision unlikely, but UUIDs are often logged, included in URLs, and visible in referrer headers — any of those can expose the value. Use a purpose-built token (e.g., from crypto.getRandomValues) for secrets.

Does UUID v7 leak sensitive information?

Yes — the first 48 bits are a Unix millisecond timestamp. Anyone with a v7 UUID can read when the record was created to the millisecond. Two v7 UUIDs also reveal how many records were created between them. Use v4 for externally visible identifiers where creation time or volume is sensitive.

Are these UUIDs cryptographically secure?

The randomness is, yes — it comes from the Web Crypto API. That said, a UUID is an identifier, not a secret; don't use one as a password or an unguessable capability token on its own.

Is UUID v4 safe to use in a public URL?

As an identifier, yes — 122 bits of randomness makes guessing infeasible. But a UUID in a URL is visible to anyone who receives the link, recorded in server logs, and can leak via the Referer header. It is not a capability on its own. Combine it with authentication for anything sensitive.

Can someone brute-force a UUID v4?

Not in practice. The search space is 2^122 — even a trillion guesses per second would take longer than the age of the universe to find a specific value. The real risks are leakage, logging, and referrer exposure, not brute force.