Remix.run Logo
molf a day ago

Good question. There's a few reasons to pick UUID over serial keys:

- Serial keys leak information about the total number of records and the rate at which records are added. Users/attackers may be able to guess how many records you have in your system (counting the number of users/customers/invoices/etc). This is a subtle issue that needs consideration on a case by case basis. It can be harmless or disastrous depending on your application.

- Serial keys are required to be created by the database. UUIDs can be created anywhere (including your backend or frontend application), which can sometimes simplify logic.

- Because UUIDs can be generated anywhere, sharding is easier.

The obvious downside to UUIDs is that they are slightly slower than serial keys. UUIDv7 improves insert performance at the cost of leaking creation time.

I've found that the data leaked by serial keys is problematic often enough; whereas UUIDs (v4) are almost always fast enough. And migrating a table to UUIDv7 is relatively straightforward if needed.

MBCook a day ago | parent | next [-]

Not only can you make a good guess at how many customers/etc exist, you can guess individual ones.

World’s easiest hack. You’re looking at /customers/3836/bills? What happens if you change that to 4000? They’re a big company. I bet that exists.

Did they put proper security checks EVERYWHERE? Easy to test.

But if you’re at /customers/{big-long-hex-string}/bill the chances of you guessing another valid ID are basically zero.

Yeah it’s security through obscurity. But it’s really good obscurity.

neya 17 hours ago | parent | next [-]

This advice assumes /customers/:id/bills is public. Protected routes shouldn't expose sensitive information such as bills anyway, so this is more of an authorization issue (who can access which resource) more than privacy concerns. So this means, if you can access customes/4000/bills, then that's an application logic issue more than the type of ID itself.

In a well designed application, you shouldn't be able to guess whether a record exists or not simply by accessing a protected URL. As a counter argument - normal BIGINT or serial PKs are performant and are more than enough for most applications.

andrewjf 9 hours ago | parent [-]

You describe a world where human skill is required to prevent these class of bugs, time and time again we've proven that people are people and bugs happen.

Systems must be _structurally architected_ with security in mind.

Security is layered, using a random key with 128-bit space makes guessing UUIDs infeasible. But _also_ you should be doing AuthZ on the records, and also you should be doing rate limiting on API so they can't be brute forced, either.

morshu9001 a day ago | parent | prev [-]

You normally aren't supposed to expose the PK anyway.

bruce511 a day ago | parent [-]

That advice was born primarily _because_ of the bigint/serial problem. If the PK is UUIDv4 then exposing the PK is less significant.

In some use cases it can be possible to exclude, or anonymize the PK, but in other cases a PK is necessary. Once you start building APIs to allow others to access your system, a UUIDv4 is the best ID.

There are some performance issues with very large tables though. If you have very large tables (think billions of rows) then UUIDv7 offers some performance benefits at a small security cost.

Personally I use v4 for almost all my tables because only a very small number of them will get large enough to matter. But YMMV.

morshu9001 13 hours ago | parent [-]

It's not about table size so much as number of joins. You don't need to trade off between security and performance if you simply expose a uuid4 secondary col on a serial PK'd table.

a day ago | parent | prev [-]
[deleted]