Tres Seaver
2011-03-15 13b95f161d36c6a19e3d90f31a5cd0ac7e46155b
Try harder to mitigate timing attacks.
2 files modified
35 ■■■■ changed files
repoze/who/plugins/htpasswd.py 27 ●●●● patch | view | raw | blame | history
repoze/who/plugins/tests/test_htpasswd.py 8 ●●●● patch | view | raw | blame | history
repoze/who/plugins/htpasswd.py
@@ -5,6 +5,11 @@
from repoze.who.interfaces import IAuthenticator
from repoze.who.utils import resolveDotted
def _padding_for_file_lines():
    yield 'aaaaaa:bbbbbb'
class HTPasswdPlugin(object):
    implements(IAuthenticator)
@@ -34,15 +39,27 @@
                return None
        result = None
        for line in f:
        maybe_user = None
        to_check = 'ABCDEF0123456789'
        # Try not to reveal how many users we have.
        # XXX:  the max count here should be configurable ;(
        lines = itertools.chain(f, _padding_for_file_lines())
        for line in itertools.islice(lines, 0, 1000):
            try:
                username, hashed = line.rstrip().split(':', 1)
            except ValueError:
                continue
            if username == login:
                if self.check(password, hashed):
                    result = username
                    # Don't bail early:  leaks information!!
            if _same_string(username, login):
                # Don't bail early:  leaks information!!
                maybe_user = username
                to_check = hashed
        # Check *something* here, to mitigate a timing attack.
        password_ok = self.check(password, to_check)
        if password_ok and maybe_user:
            result = maybe_user
        return result
    def __repr__(self):
repoze/who/plugins/tests/test_htpasswd.py
@@ -35,7 +35,9 @@
    def test_authenticate_nolines(self):
        from StringIO import StringIO
        io = StringIO()
        plugin = self._makeOne(io, None)
        def check(password, hashed):
            return True
        plugin = self._makeOne(io, check)
        environ = self._makeEnviron()
        creds = {'login':'chrism', 'password':'pass'}
        result = plugin.authenticate(environ, creds)
@@ -44,7 +46,9 @@
    def test_authenticate_nousermatch(self):
        from StringIO import StringIO
        io = StringIO('nobody:foo')
        plugin = self._makeOne(io, None)
        def check(password, hashed):
            return True
        plugin = self._makeOne(io, check)
        environ = self._makeEnviron()
        creds = {'login':'chrism', 'password':'pass'}
        result = plugin.authenticate(environ, creds)