UUIDs Should Not Replace Authorization
An IMHO on why blindly trusting UUIDs is bad
12. May 2025If data is accessible via a “public URL”, but that URL contains an identifier that’s more unique than UUID, is it really public?
This is a Tweet from @theo↗. When he proposed this statement, one reader did not agree with it, and a good old Twitter beef started.
Let me start by agreeing with Theo: If generated correctly, 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 two endpoints such an application could have when it comes to listing documents:
GET /documentswhich retrieves a list of all documentsGET /documents/<id>for retrieving a specific document
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
As it is easy to use and one less thing to worry about, developers often use AUTO_INCREMENT on a primary key. This means the first asset will have ID 1, the second one 2, and so on. Those easy-to-guess IDs are by themselves not directly a security vulnerability, merely bad design. If developers do authorization checks correctly in the backend, users cannot access each other’s documents simply by knowing an ID.
As a bad example, let’s assume the following flow:
- I upload three documents.
- They get assigned ID 3001 - 3003 in the backend.
- My browser requests
GET /documentsand I see links like:
<a href="/documents/3001">Document 1</a>
<a href="/documents/3002">Document 2</a>
<a href="/documents/3003">Document 3</a>
- I click one of the links, and my browser makes the request
GET /documents/3001. - The backend validates that my session token is still valid.
- The backend fetches document
3001and presents it to me.
So far, so normal. But what if I modify the request to be GET /documents/2999?
If the backend 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 backend assigns UUIDs, my links might look like this:
<a href="/documents/f4ba1dbd-dc5d-4ea8-8693-0f6c81b67248">Document 1</a>
<a href="/documents/6ecec59e-81f1-45e6-a9ee-e52eb4e54964">Document 2</a>
<a href="/documents/54ed780c-52cf-49c8-9284-a642dc2db99b">Document 3</a>
- I click one of the links, and my browser makes the request
GET /documents/f4ba1dbd-dc5d-4ea8-8693-0f6c81b67248. - The backend validates that my session token is still valid.
- The backend fetches document
f4ba1dbd-dc5d-4ea8-8693-0f6c81b67248and presents it to me.
As Theo points out correctly, it is now not feasible for another user, the attacker, to brute-force or guess those UUIDs.
The But
However, various web vulnerabilities and attack vectors 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 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 quiet relevant.
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. This is commonly called an authorization check.
I hope my point is somewhat understandable. If you do not agree or have something to add, feel free to reach out.
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