Enumeration mitigations
I was having a discussion with someone who suggested that due to the random component these were difficult to guess, and suitable for use as a token to access a resource. My initial reaction from reading the spec is that these would be highly vulnerable to enumeration since if I have access to some ULIDs, I can easily check +/- 1 for other ULIDs generated within the same millisecond.
Namely, if the same millisecond is detected, the random component is incremented by 1 bit in the least significant bit position (with carrying).
However, I noticed that this Go implementation specifically increments by a random amount on each millisecond collision, by default from 1 to MaxUint32:
https://pkg.go.dev/github.com/oklog/[email protected]#Monotonic
This seems like it would be useful to document the risks associated with incrementing by 1 and tradeoffs of how much additional entropy you add to each increment of the random component.
I've been thinking about the same possible improvement on the spec. I have my own C# Ulid implementation and I'm really tempted to implement a "random increment" feature.
My thoughts so far have lead me to set the span of random for the increment between 1-256. This would "waste" maximum of single byte for that feature, while making enumeration quite a bit more tiresome.
I'm not sure how actively this repository is maintained anymore, but I would love to hear any additional feedback on this feature, as I might start implementing this in my Ulid implementation at some point. But at the same time, I am a bit hesitant to deviate from the spec even more. (I already implement the random increment in a way that it would not cause exceptions)
I have also opened a ticket in my own project to discuss the implementation of this feature into my Ulid library: https://github.com/ByteAether/Ulid/issues/13