Hacker News new | past | comments | ask | show | jobs | submit login

ULID has a serious security flaw though. In order to try to improve performance / increase prefix similarity, there is a mode where it reuses the random value, and just increments it, for each new ULID.

With purely random UUID, you can be pretty sure that nobody can guess them, so they're essentially identifier and access token in one.

However once you can easily predict the next uuid from the previous one (as by incrementing the random part by one), the access token property vanishes.

At least this proposal doesn't make the same mistake, however 80 bit's isn't THAT much entropy. (Remember every bit counts with exponential growth, and 48 bits less, means 281474976710656 times easier to guess.)

So it boils down to. Would you be ok to secure all your data with 10 character one time passwords, which might not be guessed all at once, but where guessing SOME is sufficient to get owned?

Death by a thousand paper-cuts.




Thanks for the comment!

Yes, I'd like to clarify Timeflake (and many alternatives suggested) is NOT meant to be used for security.

There's an important note on the readme about this:

> Since the timestamp part is predictable, the search space within any given millisecond is 2^80 random numbers, which is meant to avoid collisions, not to secure or hide information. You should not be using timeflakes for password-reset tokens, API keys or for anything which is security sensitive. There are better libraries which are meant for this use case (for example, the standard library's secrets module).


You have to ask yourself the question though. If "URL-Safety" is a main feature. What URLs / user-data are NOT security relevant?

Leaking users private pictures / documents / whatever, will also kill a company.

I guess one could maybe circumvent this, by using a hash of the ID, but then you have to store that somewhere too, and you're back to square one.

Nevertheless, I like that you fixed the main flaw with ULID. Also you provide way better insight into the tradeoffs, so kudos!


> using a hash of the ID

Hashing the IDs won't solve their lack of entropy. Crude example: If you hash your pincode I still have only 10^4 values to try.

The easiest way to fix this is to add an access token column that is cryptographically random and use both the ID and the token in the URL.

If you trust the 80 bits already in the ID the token only needs to be 80 bits for a total of 160 bits of entropy. But if you do that you have to make sure that a missing ID and invalid token are handled identically from the attackers perspective (same reply, same timing).


> Hashing the IDs won't solve their lack of entropy.

Yes and no. It would still significantly largen the search space, as an attacker wouldn't get a point of reference to latch on to.


Which implementation are you referring to? The Go package uses crypto.random and generation blocks if the system can’t provide enough randomness. It’s possible to override this with custom implementations, but either ways it’s no less secure than a UUID unless the implementation is horrible.

The spec doesn’t specify a source of randomness - an implementation that uses https://xkcd.com/221/ will or course not be a very good one.


If you’re referring to the monotonic counter implementation, then yes, that’s a documented choice you can make if you explicitly want strictly increasing ULIDs on the same millisecond, but given that you can’t ensure this across servers anyway, it’s more an edge case.


Yes, monotonic counters, are ULID's "billion dollar mistake".

They're not an edge case, in that the standard displays them quite prominently. They're actually the only code examples given.

The standard should deprecate them, because people WILL use them "wrong".


Dunno about that. The implementations I’ve seen so far default to /dev/random and make you jump through hoops to get the monotonic counter, so if you manage to enable it they should assume it’s what you want. I’ve actually used them effectively in a couple of NoSQL problems where I didn’t need cryptographic security, just distributed timestamps - I was creating two sequential events (create and update), and they had to always order one after another, even though I created them in the same loop and millisecond.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: