Another way to solve enumeration attacks is to use a two-way hashing algorithm to convert your auto-incrementing integer IDs to a hash of arbitrary length. This is essentially what YouTube is doing with their video IDs and it's a low-CPU, low-complexity solution that prevents/severely deters enumeration attacks.
Hashids sounds like hashing but it‘s just encoding and decoding the values. I shared that approach in the article. They have the drawback that you always need to decode manually the url when you e.g. want to lookup a record from an URL.
> ULID [...]: These libraries generate special random strings to use as unique identifiers. They need more space than binary UUIDs when manual binary conversions are not implemented. Looking up these values manually in the database is complicated as binary data needs to be copied to database management tools and queries.
Though not compatible with any version of UUID, ULIDs take up the exact same binary width (128-bits) and the binary data can be stored in a standard UUID field. It's not that complicated to use the "UUID-like form" of a ULID in database tools and conversions between the standard ULID base-32 string forma and "UUID format" are easily automated. Many of the ULID libraries will give you at least the most straightforward "UUID format" for you (with the bytes in the same order in UUID form and ULID form).
The biggest complication there is UUID sort orders which are very different from ULID sort orders. It can be useful in a database to sort them the same as that can benefit indexing and clustering (making properly sorted ULIDs in UUID columns again useful for PKs). I put the work in to do the weirder conversion from ULID to UUID form best for SQL Server ordering (long story very short: the first six bytes of the ULID in the last six bytes of an SQL Server intended UUID). It's a small amount of code with a lot more comments than code explaining why that sort order matters (and linking to this reference blog post: https://devblogs.microsoft.com/oldnewthing/20190426-00/?p=10...).
4 comments
[ 5.3 ms ] story [ 26.5 ms ] threadHere is one such library for C# https://github.com/ullmark/hashids.net
Do you? Why not just also store the hashid in the row as an “external ID”?
Though not compatible with any version of UUID, ULIDs take up the exact same binary width (128-bits) and the binary data can be stored in a standard UUID field. It's not that complicated to use the "UUID-like form" of a ULID in database tools and conversions between the standard ULID base-32 string forma and "UUID format" are easily automated. Many of the ULID libraries will give you at least the most straightforward "UUID format" for you (with the bytes in the same order in UUID form and ULID form).
The biggest complication there is UUID sort orders which are very different from ULID sort orders. It can be useful in a database to sort them the same as that can benefit indexing and clustering (making properly sorted ULIDs in UUID columns again useful for PKs). I put the work in to do the weirder conversion from ULID to UUID form best for SQL Server ordering (long story very short: the first six bytes of the ULID in the last six bytes of an SQL Server intended UUID). It's a small amount of code with a lot more comments than code explaining why that sort order matters (and linking to this reference blog post: https://devblogs.microsoft.com/oldnewthing/20190426-00/?p=10...).