Obfuscating primary keys
Often you'll want urls that are not authenticated, yet are not easily guessed. Giving each of your models a UUID is one approach. Another elegant approach is to use a block cipher to "encrypt" a unique identifier like the primary key (PK).
Let's sketch out what properties a good solution for PK obfuscation has:
- Generates a one to one mapping between all PKs and obfuscated PKs, with no possibility of collision.
- Not easily possible for an attacker to determine the obfuscated PK given a PK and vice versa.
- Reversible, i.e. the originator can determine the original PK given an obfuscated PK.
Heyyy... that looks an awful lot like encryption!
You can use pretty much any encryption mechanism. In the case of int primary keys, using a 32 or 64 bit cipher has the nice property that the obfuscated PK can also be stored in 32 or 64 bits. The example below uses a skip32 cipher and converts it into a compressed number representation (OBFUSCATE_ALPHABET
) for human readability. The example stores the obfuscated PK as well, but note that this is not strictly necessary, as long as you keep
The cipher key (SKIP32_KEY
) determines the unique mapping of PKs to obfuscated PKs. Keep it secret! Keep it safe!
Primary Sources:
- http://blog.notdot.net/2007/9/Damn-Cool-Algorithms-Part-2-Secure-permutations-with-block-ciphers
- http://www.quora.com/How-do-you-symmetrically-encrypt-32-bit-auto-increment-IDs-to-avoid-using-64-bit-UUIDs-to-conceal-the-size-and-order-of-a-database-table
- http://www.quora.com/Is-exposing-database-auto-increment-id-considered-a-bad-practice
Other resources:
- http://programmers.stackexchange.com/questions/237112/randomized-hash-function-with-no-collisions
- http://stackoverflow.com/questions/7797488/how-to-use-skipjack-skip32-in-java-to-randomize-sequential-integers-from-a-dat
- http://crypto.stackexchange.com/questions/504/can-you-create-a-strong-blockcipher-with-small-blocksize-given-a-strong-blockci