mkiesel.ch

UUIDs Should Not Replace Authorization

2025.05.12

Today, I came across a Tweet↗ from @theo↗:

If data is accessible via a “public URL”, but that URL contains an identifier that’s more unique than UUID, is it really public?

One reader did not agree with that statement, and a good old Twitter beef started.

Let me start by agreeing with Theo: UUIDs are unique and practically unguessable through brute force. However, in my humble opinion, blindly trusting this mindset can result in security vulnerabilities. As someone who finds and reports such vulnerabilities, I need to make a case for why this mindset is bad.

The Setup 

To demonstrate my point, let’s assume we have a simple document management system in the form of a web application. Here are a few endpoints such an application could have:

If possible, always put sensitive IDs into a POST request body rather than into a URL or a query parameter. This stops referer leaks and usually, bodies do not get logged onto disk by reverse proxies and other systems in between.

The classic way of (not) doing this 

Until UUIDs gained popularity, developers would often use incrementing, simple, and numeric IDs when creating an asset. The first document might have had document ID 1000, the second one 1001, …

Those easy-to-guess IDs are by themselves not directly a security vulnerability, merely bad design. If you do authorization checks correctly in the back end, users cannot access each other’s documents simply by knowing an ID.

As a bad example, let’s assume the following flow:  

<a class="doc-link" href="/documents/3001">Document 1</a>
<a class="doc-link" href="/documents/3002">Document 2</a>
<a class="doc-link" href="/documents/3003">Document 3</a>

So far, so normal. But what if I modify the request to be GET /documents/2999?

If the back end returns the document 2999, this is a clear security vulnerability known as an Insecure Direct Object References↗ (IDOR). I should not be able to access other users’ assets just by knowing their ID. And that is exactly where, in theory, UUIDs come into play.

The UUID way 

Now, if we do the same flow, but the back end assigns UUIDs, my links might look like this:

<a class="doc-link" href="/documents/f4ba1dbd-dc5d-4ea8-8693-0f6c81b67248">Document 1</a>
<a class="doc-link" href="/documents/6ecec59e-81f1-45e6-a9ee-e52eb4e54964">Document 2</a>
<a class="doc-link" href="/documents/54ed780c-52cf-49c8-9284-a642dc2db99b">Document 3</a>

As Theo points out correctly, it is now not feasible for another user, the attacker, to brute-force/guess those UUIDs.

The But 

Various web vulnerabilities exist that, given the right setup, would potentially enable an attacker to leak your UUIDs.

HTTP request smuggling 

Using “HTTP request smuggling”, it might be possible to leak other users’ requests↗. This depends on various architecture decisions like web server, proxies, and lastly, request structure on how UUIDs are fetched. For a good write-up, check out portswigger.net↗.

XSSi 

Cross Site Script Inclusion (XSSi)↗ is, depending on the setup, an even better/easier way of leaking UUIDs. If your web framework generates dynamic JavaScript files and the UUIDs to your document are in such JS files, your website might be vulnerable to XSSi. A great write-up on this vulnerability can be found on sidechannel.blog↗.

XSS 

Using Cross Site Scripting↗, often called XSS, an attacker can leak the UUIDs back to them in various ways by injecting JavaScript into a page that is then visited by the user. Although web frameworks like Angular and libraries like DOMPurify↗ make it harder year by year, XSS vulnerabilities are still quite relevant. They might even gain more popularity again as some LLMs produce literal garbage security vulnerabilities when you are just vibing and have no clue what you are doing.

This is not the best example for an attack vector. If the XSS payload window is big enough to allow for UUID exfiltration, it is probably big enough to ride the session of the user. (aka. “do anything”)

Since an attacker has control of the session in the user’s browser, no ID protects against leaking the documents here. The attacker can download the documents into the DOM and exfiltrate the complete documents rather than just the IDs. How documents and IDs can be leaked to the attacker depends on the Content Security Policy (CSP)↗ in place.

The Result 

If you trust the mindset “UUIDs are not guessable and thus secure”, an attacker who leaked UUIDs of your users using any of the above methods might simply download documents that he should not have access to.

On every single request, validate that the user requesting an asset has the correct permissions/ownership of the asset (Authorization check)

I hope my point is somewhat understandable. If you do not agree or have something to add, feel free to reach out via the social media links from the header or leave a comment via BlueSky.

Thanks to floyd@chaos.social↗ for input on XSSi and highlighting that XSS is not the best example for leaking UUIDs. Critique is always welcome!

Cheers

Comments

To comment on this blog post, visit Bluesky↗.