Package: django-encrypted-id
Note: Encrypted IDs generated by version 0.3.0 onwards will be different
from those generated by version 0.2.0. But versions 0.3.x will decrypt the IDs
generated by the version 0.2.0.Note: Version 0.2.0 is a breaking change from versions0.1.x.
If you’ve been usingekeyin permalinks, then it is recommended for you to
not upgrade to 0.2.x.Consider this example model:fromdjango.dbimportmodelsfromencrypted_id.modelsimportEncryptedIDModelclassFoo(EncryptedIDModel):text=models.TextField()By inheriting fromEncryptedIDModel, you get .ekey as a property on your
model instances. This is how they will look like:In[1]:fromtapp.modelsimportFooIn[2]:f=Foo.objects.create(text="asd")In[3]:f.idOut[3]:1In[4]:f.ekeyOut[4]:'bxuZXwM4NdgGauVWR-ueUA'Youcandoreverselookup:In[5]:fromencrypted_idimportdecodeIn[6]:decode(f.ekey)Out[6]:1If you can not inherit from the helper base class, no problem, you can just use
theekey()function fromencrypted_idpackage:In[7]:fromencrypted_idimportekeyIn[8]:fromdjango.contrib.auth.modelsimportUserIn[9]:ekey(User.objects.get(pk=1))Out[9]:'bxuZXwM4NdgGauVWR-ueUA'To do the reverse lookup, you have two helpers available. First is provided byEncryptedIDManager, which is used by default if you inherit fromEncryptedIDModel, and have not overwritten the.objects:In[10]:Foo.objects.get_by_ekey(f.ekey)Out[10]:<Foo:Fooobject>But sometimes you will prefer the form:In[11]:Foo.objects.get_by_ekey_or_404(f.ekey)Out[11]:<Foo:Fooobject>Which works the same, but instead of raisingDoesNotExist, it raisesHttp404, so it can be used in views.You your manager is not inheriting fromEncryptedIDManager, you can use:In[12]:e=ekey(User.objects.first())In[13]:eOut[13]:'bxuZXwM4NdgGauVWR-ueUA'In[14]:get_object_or_404(User,e)Out[14]:<User:amitu>encrypted_id.get_object_or_404, as well asEncryptedIDManager.get_by_ekeyandEncryptedIDManager.get_by_ekey_or_404take extra keyword argument, that can
be used to filter if you want.If you are curious, the regex used to match the generated ids is:"[0-9a-zA-Z-_]+"If you are usingsmarturls, you can use URL
pattern like:"/<ekey:foo>/"I recommend this usage of encrypted-id over UUID, as UUIDs have significant
issues that should be considered (tldr: they take more space on disk and RAM,
and have inferior indexing than integer ids), and if your goal is simply to
make URLs non guessable, encrypted id is a superior approach.If you are curious about the encryption used: I am usingAES, frompycryptolibrary, and am usingSECRET_KEYfor password
(SECRET_KEY[:32]) andIV(first 16 characters of hash ofSECRET_KEYand asub_key), in theAES.CBCmode. Thesub_keyis taken from the
model’sMetaattributeek_key, or simplydb_tableifek_keyis
not set.In general it is recommended not to have staticIV, butCBCoffsets
some of the problems with having static IV.  What is the the issue with static
IV you ask: if plain text “abc” and “abe” are encrypted, the first two bytes
would be same.  Now this does not present a serious problem for us, as the
plain text that I am encrypting usesCRC32in the beginning of payload, so
even if you have ids, 1, 11, an attacker can not say they both start with same
first character.The library also supports the scenario that you have to cycleSECRET_KEYdue to some reason, so URLs encrypted with olderSECRET_KEYcan still be
decoded after you have changed it (as long as you store old versions inSECRET_KEYSsetting).  In order to decrypt the library tries each secret
key, and compares theCRC32of data to know for sure (as sure as things get
in such things), that we have decrypted properly.Do feel free to raise an issue here, if you face any issues, I would be happy
to help. The library supports both python 2.7 and 3.5, as well as it all
versions of django that django team supports.