Tamper-proof python dictionary
Sometimes the need arises to pass a python list, tuple, dictionary, etc. from one page to another. If you do not have a session service to hold it, the object may need to pass through a HTTP request (in cookie or get/post param), opening the possibility of tampering. Without some precautions, a serious vulnerability could be exposed. Googling for “dictionary to string” yields some example implementations, most of which are still vulnerable to attack. I think we can do better.
Option 1: encode=string-ify, decode=eval
The idea here is take a dict and turn it into a string. Then running eval would give you back the original dictionary. If the world were full of with people who danced in flower-filled meadows, this would be ok. The problem is that if you need to pass “s” through an HTTP request, it can be altered into something, like perhaps “os.rmdir(’/’)”
d={'var1':1, 'var2':2} s="{'var1':1, 'var2':2}" new_d = eval(s)
Result: VULNERABLE to arbitrary code execution.
Option 2: encode=string-ify, decode=slightly-safter eval
The eval method in python does allow you to tighten it up a bit. There are several articles detailing how to make it slightly safer, by only allowing a certain subset of methods to be available.
new_d = eval(s, {"__builtins__":None}, {})
Result: VULNERABLE to arbitrary code execution.
Option 3: encode=pickle, decode=unpickle
So what if instead of just eval’ing, we used the pickle module to serialize and then deserialize the dictionary? That feels safer at first glance. But what if the object to be deserialized was no longer a dictionary? Perhaps an attacker pickled their own evil object, whose constructor or maybe get() method was modified to do wreak havoc.
Result: VULNERABLE to arbitrary code execution.
Option 4: encode=jsonify, decode=un-jsonify
One technique that didn’t appear in the results was using simplejson to encode and decode. (Thanks Mike Pirnat for suggesting the now-obvious). The simplejson decoder is a much safer one, because it scans the input to make sure it really does look like a dictionary (with all keys and values being safe types) before turning it into a python dict.
User’s can still tamper with the keys and values, but it is no more susceptible than other types of parameter manipulation rampant on the web, so validation still need be there.
Result: much better
Option 5: encode=jsonify(jsonify+hash), decode=un-jsonify plus hash check
To an extra level of comfort, let’s try to make sure that the dictionary wasn’t tampered with. We can take a hash of what we just json-ified, pack it with the data, and json-ify that result. Then when we unpack it, we can double-check that the hash that came with the data still matches a new calculation of the hash of the data. Assuming we have a strong, protected key for our hash algorithm, if the user tried to tamper with the data, they wouldn’t be able to replicate the corresponding change in the hash value, allowing us to raise an exception.
Result: best yet
There ye have it, a safer way of turning a dictionary into a string suitable for passing around the website. Just make sure your hash function is strong and your key is protected.
You can download encode_dict.py to see the final result.
- Filed under software development
- Tagged with python, web security
- Comments(0)
