In our previous article, we explored the basics of using the Obfuskey
class to reversibly obfuscate single integer IDs. This is a great pattern for cleaning up simple, sequential database IDs.
But what if you need to do more? What if you have multiple pieces of information you want to embed into a single, compact key? This is where the true power of the package shines through with the Obfusbit
class.
Obfusbit
allows you to pack multiple integer values into a single, obfuscated key string. This is perfect for when you need to embed several pieces of information, such as a user ID and a product ID, into a single token, which can lead to more efficient API calls and fewer database lookups.
Where to Find Obfuskey
You can find the full source code, documentation, and open an issue on the project's GitHub repository. Obfuskey on GitHub
Packing Multiple Values with Obfusbit
To get started with Obfusbit
, you define a "schema" that specifies the name and bit length for each value you want to pack. Let's use the BASE62
alphabet, which is a great default for URL-friendly keys as it includes both uppercase and lowercase letters and digits.
from obfuskey import Obfuskey, Obfusbit, alphabets
# Define a schema for your data
product_schema = [
{"name": "category_id", "bits": 4}, # Max value 15
{"name": "item_id", "bits": 20}, # Max value ~1 million
{"name": "status", "bits": 3}, # Max value 7
]
# Initialize Obfusbit with a schema and BASE62 alphabet
obfkey = Obfuskey(alphabets.BASE62, key_length=10)
obfuscator = Obfusbit(product_schema, obfuskey=obfkey)
# Pack your values
packed_key = obfuscator.pack(values={"category_id": 5, "item_id": 123456, "status": 1})
print(f"Packed Key: {packed_key}") # 42930689
# Unpack the key to retrieve the original values
unpacked_values = obfuscator.unpack(packed_key)
print(f"Unpacked Values: {unpacked_values}") # {'status': 1, 'item_id': 123456, 'category_id': 5}
A More Advanced Use Case: Packing UUIDs and Saving Database Lookups
While Obfuskey
is fantastic for integer IDs, what about universally unique identifiers (UUIDs)? Many modern applications use UUIDs as primary keys to avoid collisions in distributed systems.
This 128-bit integer representation of a UUID is exactly what Obfusbit
can work with. The key insight here is that you can pack a UUID into an Obfusbit key string, and when you unpack it, you get the original integer back, which can then be converted back to a UUID object.
For this advanced use case, we'll stick with the robust BASE62
alphabet for its excellent balance of key length and character set.
import uuid
from obfuskey import Obfuskey, Obfusbit, alphabets
# Define a schema with a UUID and a small integer
# A UUID is 128 bits long
product_schema = [
{"name": "category_id", "bits": 8},
{"name": "product_uuid", "bits": 128},
]
# Initialize Obfusbit with a BASE62 alphabet and a key length of 30
# This combination gives us plenty of room for our 136 bits of data
obfkey = Obfuskey(alphabets.BASE62, key_length=30)
obfuscator = Obfusbit(product_schema)
# Let's mock a database lookup
category_id = 42
product_uuid = uuid.uuid4()
# Pack the values into a single key
values = {"category_id": category_id, "product_uuid": product_uuid.int}
packed_key = obfuscator.pack(values=values)
print(f"Packed Key: {packed_key}")
# On the server, we unpack it
unpacked_values = obfuscator.unpack(packed_key)
retrieved_uuid = uuid.UUID(int=unpacked_values["product_uuid"])
print(f"Retrieved UUID: {retrieved_uuid}")
How Does This Save Database Lookups?
This is where the real power comes in. In a traditional setup, if you have a URL like /products/books/123e4567...
, you would have to perform a database lookup on the products
table using the UUID. Then, you might need another lookup on the categories
table to verify the category.
With Obfusbit
, you can embed this information directly into the URL. You unpack the key and instantly have the category_id
and the product_uuid
. This means you can validate the category and perform your single product lookup without an additional database query. The key becomes a self-contained token of data, making your API more efficient and your code cleaner.
More Than Just a UUID: Squeezing More into Your Keys
You can pack additional bits of data, as long as the total bit length of your schema fits within the capacity of your chosen key length and alphabet. For example, packing multiple UUIDs into a single key.
Let's use the BASE58
alphabet for our final example. It's often used for cryptocurrencies and is very similar to BASE56
but excludes the ambiguous characters for better transcription and manual entry.
import uuid
from obfuskey import Obfuskey, Obfusbit, alphabets
# Schema for a user's interaction with a product
interaction_schema = [
{"name": "user_uuid", "bits": 128},
{"name": "product_uuid", "bits": 128},
{"name": "interaction_type", "bits": 3}, # e.g., 1=like, 2=review
]
# Total bits: 128 + 128 + 3 = 259 bits
# We need an even larger key capacity. A key length of 45 with BASE58 will do.
obfkey = Obfuskey(alphabets.BASE58, key_length=45)
obfuscator = Obfusbit(interaction_schema, obfuskey=obfkey)
# Mock two UUIDs and an interaction type
user_uuid = uuid.uuid4()
product_uuid = uuid.uuid4()
interaction_type = 1 # A 'like'
values = {
"user_uuid": user_uuid.int,
"product_uuid": product_uuid.int,
"interaction_type": interaction_type,
}
packed_key = obfuscator.pack(values=values)
print(f"Packed Key (Base58): {packed_key}")
unpacked_values = obfuscator.unpack(packed_key)
retrieved_user_uuid = uuid.UUID(int=unpacked_values["user_uuid"])
retrieved_product_uuid = uuid.UUID(int=unpacked_values["product_uuid"])
assert retrieved_user_uuid == user_uuid
assert retrieved_product_uuid == product_uuid
Important Considerations
- Not for Security: Remember, Obfuskey is not a cryptographic library. It's designed to make your IDs non-sequential and less predictable, not to protect sensitive information from being decoded.
-
Key Length and Alphabet: The combination of your chosen alphabet and key length determines the maximum integer value that can be obfuscated. You can calculate this with the formula:
(len(alphabet) ** key_length) - 1
. Make sure this value is large enough to handle your highest possible ID.
Conclusion
Obfusbit
is a simple yet powerful tool for any Python developer who needs to create "pretty" and secure-looking, but easily reversible, identifiers. By leveraging its flexible alphabet system and powerful bit-packing capabilities, you can clean up your URLs, reduce database queries, and build more performant, elegant, and secure-looking applications.
Stay tuned for the next articles in the series, where we'll explore how to use Obfuskey in JavaScript/TypeScript and, finally, how to build a full-stack, cross-language API with these tools.
Top comments (0)