Glenn Linderman <v+pyt...@g.nevcal.com> added the comment:

Given Martin's comment (msg150832) I guess I should add my suggestion to this 
issue, at least for the record.

Rather than change hash functions, randomization could be added to those dicts 
that are subject to attack by wanting to store user-supplied key values.  The 
list so far seems to be   urllib.parse, cgi, json  Some have claimed there are 
many more, but without enumeration.  These three are clearly related to the 
documented issue.

The technique would be to wrap dict and add a short random prefix to each key 
value, preventing the attacker from supplier keys that are known to collide... 
and even if he successfully stumbles on a set that does collide on one request, 
it is unlikely to collide on a subsequent request with a different prefix 
string.

The technique is fully backward compatible with all applications except those 
that contain potential vulnerabilities as described by the researchers. The 
technique adds no startup or runtime overhead to any application that doesn't 
contain the potential vulnerabilities.  Due to the per-request randomization, 
the complexity of creating a sequence of sets of keys that may collide is 
enormous, and requires that such a set of keys happen to arrive on a request in 
the right sequence where the predicted prefix randomization would be used to 
cause the collisions to occur.  This might be possible on a lightly loaded 
system, but is less likely on a system with heavy load, which are more 
interesting to attack.

Serhiy Storchaka provided a sample implementation on the python-dev, copied 
below, and attached as a file (but is not a patch).

# -*- coding: utf-8 -*-
from collections import MutableMapping
import random


class SafeDict(dict, MutableMapping):

    def __init__(self, *args, **kwds):
        dict.__init__(self)
        self._prefix = str(random.getrandbits(64))
        self.update(*args, **kwds)

    def clear(self):
        dict.clear(self)
        self._prefix = str(random.getrandbits(64))

    def _safe_key(self, key):
        return self._prefix + repr(key), key

    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, self._safe_key(key))
        except KeyError as e:
            e.args = (key,)
            raise e

    def __setitem__(self, key, value):
        dict.__setitem__(self, self._safe_key(key), value)

    def __delitem__(self, key):
        try:
            dict.__delitem__(self, self._safe_key(key))
        except KeyError as e:
            e.args = (key,)
            raise e

    def __iter__(self):
        for skey, key in dict.__iter__(self):
            yield key

    def __contains__(self, key):
        return dict.__contains__(self, self._safe_key(key))

    setdefault = MutableMapping.setdefault
    update = MutableMapping.update
    pop = MutableMapping.pop
    popitem = MutableMapping.popitem
    keys = MutableMapping.keys
    values = MutableMapping.values
    items = MutableMapping.items

    def __repr__(self):
        return '{%s}' % ', '.join('%s: %s' % (repr(k), repr(v))
            for k, v in self.items())

    def copy(self):
        return self.__class__(self)

    @classmethod
    def fromkeys(cls, iterable, value=None):
        d = cls()
        for key in iterable:
            d[key] = value
        return d

    def __eq__(self, other):
        return all(k in other and other[k] == v for k, v in self.items()) and \
            all(k in self and self[k] == v for k, v in other.items())

    def __ne__(self, other):
        return not self == other

----------
Added file: http://bugs.python.org/file24169/SafeDict.py

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue13703>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to