Beispiel #1
0
def _compare_salted_tokens(request_csrf_token, csrf_token):
    # Assume both arguments are sanitized -- that is, strings of
    # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS.
    return constant_time_compare(
        _unsalt_cipher_token(request_csrf_token),
        _unsalt_cipher_token(csrf_token),
    )
Beispiel #2
0
def get_user(request):
    """
    Return the user model instance associated with the given request session.
    If no user is retrieved, return an instance of `AnonymousUser`.
    """
    from .models import AnonymousUser
    user = None
    try:
        user_id = _get_user_session_key(request)
        backend_path = request.session[BACKEND_SESSION_KEY]
    except KeyError:
        pass
    else:
        if backend_path in settings.AUTHENTICATION_BACKENDS:
            backend = load_backend(backend_path)
            user = backend.get_user(user_id)
            # Verify the session
            if hasattr(user, 'get_session_auth_hash'):
                session_hash = request.session.get(HASH_SESSION_KEY)
                session_hash_verified = session_hash and constant_time_compare(
                    session_hash, user.get_session_auth_hash())
                if not session_hash_verified:
                    request.session.flush()
                    user = None

    return user or AnonymousUser()
Beispiel #3
0
 def unsign(self, signed_value):
     if self.sep not in signed_value:
         raise BadSignature('No "%s" found in value' % self.sep)
     value, sig = signed_value.rsplit(self.sep, 1)
     if constant_time_compare(sig, self.signature(value)):
         return value
     raise BadSignature('Signature "%s" does not match' % sig)
Beispiel #4
0
    def check_token(self, user, token):
        """
        Check that a password reset token is correct for a given user.
        """
        if not (user and token):
            return False
        # Parse the token
        try:
            ts_b36, hash = token.split("-")
        except ValueError:
            return False

        try:
            ts = base36_to_int(ts_b36)
        except ValueError:
            return False

        # Check that the timestamp/uid has not been tampered with
        if not constant_time_compare(self._make_token_with_timestamp(user, ts), token):
            return False

        # Check the timestamp is within limit. Timestamps are rounded to
        # midnight (server time) providing a resolution of only 1 day. If a
        # link is generated 5 minutes before midnight and used 6 minutes later,
        # that counts as 1 day. Therefore, PASSWORD_RESET_TIMEOUT_DAYS = 1 means
        # "at least 1 day, could be up to 2."
        if (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS:
            return False

        return True
Beispiel #5
0
def login(request, user, backend=None):
    """
    Persist a user id and a backend in the request. This way a user doesn't
    have to reauthenticate on every request. Note that data set during
    the anonymous session is retained when the user logs in.
    """
    session_auth_hash = ''
    if user is None:
        user = request.user
    if hasattr(user, 'get_session_auth_hash'):
        session_auth_hash = user.get_session_auth_hash()

    if SESSION_KEY in request.session:
        if _get_user_session_key(request) != user.pk or (
                session_auth_hash and not constant_time_compare(
                    request.session.get(HASH_SESSION_KEY, ''),
                    session_auth_hash)):
            # To avoid reusing another user's session, create a new, empty
            # session if the existing session corresponds to a different
            # authenticated user.
            request.session.flush()
    else:
        request.session.cycle_key()

    try:
        backend = backend or user.backend
    except AttributeError:
        backends = _get_backends(return_tuples=True)
        if len(backends) == 1:
            _, backend = backends[0]
        else:
            raise ValueError(
                'You have multiple authentication backends configured and '
                'therefore must provide the `backend` argument or set the '
                '`backend` attribute on the user.')
    else:
        if not isinstance(backend, str):
            raise TypeError(
                'backend must be a dotted import path string (got %r).' %
                backend)

    request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
    request.session[BACKEND_SESSION_KEY] = backend
    request.session[HASH_SESSION_KEY] = session_auth_hash
    if hasattr(request, 'user'):
        request.user = user
    rotate_token(request)
    user_logged_in.send(sender=user.__class__, request=request, user=user)
Beispiel #6
0
 def decode(self, session_data):
     encoded_data = base64.b64decode(force_bytes(session_data))
     try:
         # could produce ValueError if there is no ':'
         hash, serialized = encoded_data.split(b':', 1)
         expected_hash = self._hash(serialized)
         if not constant_time_compare(hash.decode(), expected_hash):
             raise SuspiciousSession("Session data corrupted")
         else:
             return self.serializer().loads(serialized)
     except Exception as e:
         # ValueError, SuspiciousOperation, unpickling exceptions. If any of
         # these happen, just return an empty dictionary (an empty session).
         if isinstance(e, SuspiciousOperation):
             logger = logging.getLogger('server.security.%s' % e.__class__.__name__)
             logger.warning(str(e))
         return {}
Beispiel #7
0
    def _decode(self, data):
        """
        Safely decode an encoded text stream back into a list of messages.

        If the encoded text stream contained an invalid hash or was in an
        invalid format, return None.
        """
        if not data:
            return None
        bits = data.split('$', 1)
        if len(bits) == 2:
            hash, value = bits
            if constant_time_compare(hash, self._hash(value)):
                try:
                    # If we get here (and the JSON decode works), everything is
                    # good. In any other case, drop back and return None.
                    return json.loads(value, cls=MessageDecoder)
                except json.JSONDecodeError:
                    pass
        # Mark the data as used (so it gets removed) since something was wrong
        # with the data.
        self.used = True
        return None
Beispiel #8
0
 def verify(self, password, encoded):
     crypt = self._load_library()
     algorithm, salt, data = encoded.split('$', 2)
     assert algorithm == self.algorithm
     return constant_time_compare(data, crypt.crypt(password, data))
Beispiel #9
0
 def verify(self, password, encoded):
     if len(encoded) == 37 and encoded.startswith('md5$$'):
         encoded = encoded[5:]
     encoded_2 = self.encode(password, '')
     return constant_time_compare(encoded, encoded_2)
Beispiel #10
0
 def verify(self, password, encoded):
     encoded_2 = self.encode(password, '')
     return constant_time_compare(encoded, encoded_2)
Beispiel #11
0
 def verify(self, password, encoded):
     algorithm, salt, hash = encoded.split('$', 2)
     assert algorithm == self.algorithm
     encoded_2 = self.encode(password, salt)
     return constant_time_compare(encoded, encoded_2)
Beispiel #12
0
 def verify(self, password, encoded):
     algorithm, data = encoded.split('$', 1)
     assert algorithm == self.algorithm
     encoded_2 = self.encode(password, data.encode('ascii'))
     return constant_time_compare(encoded, encoded_2)