def check_token(self, user, token): """ Check that a password reset token is correct for a given user. """ # 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): # Fallback to Django 1.2 method for compatibility. # PendingDeprecationWarning <- here to remind us to remove this in # Django 1.5 if not constant_time_compare(self._make_token_with_timestamp_old(user, ts), token): return False # Check the timestamp is within limit if (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS: return False return True
def rauth(request): """ An implementation of Jille Timmermans' rauth scheme The token that is given to the authenticated user is only valid until the end of the day. """ if request.REQUEST.get('url') is None: raise Http404 if (request.REQUEST.get('validate') is not None and request.REQUEST.get('user') is not None): token = sha256('%s|%s|%s|%s' % ( request.REQUEST['user'], date.today(), request.REQUEST['url'], settings.SECRET_KEY)).hexdigest() if constant_time_compare(request.REQUEST['validate'], token): return HttpResponse("OK") return HttpResponse("INVALID") ''' The next check will allow you to request information about the user that is currently logged in using the 'fetch'-get attribute with the property names seperated by commas. A JSON string will be returned containing the information. ''' if (request.REQUEST.get('fetch') is not None and request.REQUEST.get('user') is not None): token = sha256('%s|%s|%s|%s' % ( request.REQUEST['user'], date.today(), request.REQUEST['url'], settings.SECRET_KEY)).hexdigest() if constant_time_compare(request.REQUEST['token'], token): user = Es.by_name(request.REQUEST['user']) properties = { 'firstname': user.first_name, 'lastname': user.last_name, 'fullname': user.full_name, 'groups': list(user.cached_groups_names) } return HttpResponse(json.dumps(dict([ (k, properties[k]) for k in set(s.strip() for s in request.REQUEST.get('fetch').split(',')) if k in properties ]))) return HttpResponse("INVALID TOKEN") if not request.user.is_authenticated(): return redirect_to_login('%s?url=%s' % ( reverse('rauth'), urlquote(request.REQUEST['url']))) token = sha256('%s|%s|%s|%s' % (str(request.user.name), date.today(), request.REQUEST['url'], settings.SECRET_KEY)).hexdigest() return HttpResponseRedirect('%s%suser=%s&token=%s' % ( request.REQUEST['url'], '?' if request.REQUEST['url'].find('?') == -1 else '&', str(request.user.name), token))
def flag_is_correct(self, flag): """ Test if a flag matches the challenge metadata. """ expected = self.flag.lower() provided = flag.strip().lower() wrapped = 'flag{%s}' % (provided,) if constant_time_compare(expected, provided) or \ constant_time_compare(expected, wrapped): return True else: return False
def clean_hash(self): hash = self.cleaned_data["hash"] if not constant_time_compare(hash, self.make_hash(self.data)): raise ValidationError("Tamper alert") return hash
def wrapper(request, *args, **kwargs): authentication = app_settings.RECURLY_WEBHOOK_HTTP_AUTHENTICATION # If the user has not setup settings.RECURLY_WEBHOOK_HTTP_AUTHENTICATION then # we trust they are doing it at the web server level. if authentication is None: return fn(request, *args, **kwargs) try: method, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1) except KeyError: response = HttpResponse() response.status_code = 401 response['WWW-Authenticate'] = 'Basic realm="Restricted"' return response try: if method.lower() != 'basic': raise ValueError() if not constant_time_compare(auth.strip().decode('base64'), authentication): return HttpResponseForbidden() except Exception: return HttpResponseBadRequest() return fn(request, *args, **kwargs)
def check_phpsessid(self, request): if request.user.is_authenticated: backend = auth.load_backend(request.session[auth.BACKEND_SESSION_KEY]) if not isinstance(backend, FlourishSessionBackend): return if not constant_time_compare(request.session['PHPSESSID'], request.COOKIES.get('PHPSESSID')): # The user has changed session or logged out without us knowing. # This should not happen. Clean up the both sessions just in case. auth.logout(request) return try: rawphpsessid = request.COOKIES['PHPSESSID'] except KeyError: return # Try to authenticate this user. It's pretty likely that # FlourishSessionBackend will succeed, but not guaranteed. # # Change the order of AUTHENTICATION_BACKENDS if this causes # you problems. user = auth.authenticate(request, rawphpsessid=rawphpsessid) if user: request.user = user auth.login(request, user)
def get_user(request): """ Returns the user model instance associated with the given request session. If no user is retrieved an instance of `AnonymousUser` is returned. """ 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()
def process_view(self, request, view_func, args, kwargs): """Check the CSRF token if this is a POST.""" if getattr(request, "csrf_processing_done", False): return # Allow @csrf_exempt views. if getattr(view_func, "csrf_exempt", False): return # Bail if this isn't a POST. if request.method != "POST": return self._accept(request) # The test client uses this to get around CSRF processing. if getattr(request, "_dont_enforce_csrf_checks", False): return self._accept(request) # Try to get the token from the POST and fall back to looking at the # X-CSRFTOKEN header. user_token = request.POST.get("csrfmiddlewaretoken", "") if user_token == "": user_token = request.META.get("HTTP_X_CSRFTOKEN", "") request_token = getattr(request, "csrf_token", "") # Check that both strings aren't empty and then check for a match. if not ((user_token or request_token) and crypto.constant_time_compare(user_token, request_token)): reason = django_csrf.REASON_BAD_TOKEN django_csrf.logger.warning( "Forbidden (%s): %s" % (reason, request.path), extra=dict(status_code=403, request=request) ) return self._reject(request, reason) else: return self._accept(request)
def project_key_from_auth(self, auth): if not auth.public_key: raise APIUnauthorized('Invalid api key') # Make sure the key even looks valid first, since it's # possible to get some garbage input here causing further # issues trying to query it from cache or the database. if not ProjectKey.looks_like_api_key(auth.public_key): raise APIUnauthorized('Invalid api key') try: pk = ProjectKey.objects.get_from_cache(public_key=auth.public_key) except ProjectKey.DoesNotExist: raise APIUnauthorized('Invalid api key') # a secret key may not be present which will be validated elsewhere if not constant_time_compare(pk.secret_key, auth.secret_key or pk.secret_key): raise APIUnauthorized('Invalid api key') if not pk.is_active: raise APIUnauthorized('API key is disabled') if not pk.roles.store: raise APIUnauthorized('Key does not allow event storage access') return pk
def logout(request): name = request.GET.get('app', '') service = Service.objects.filter(name=name).first() if not service: return redirect('/') time = request.GET.get('time', '0') time = int(time) if time.isdigit() else 0 date = datetime.datetime.fromtimestamp(time, timezone.utc) now = timezone.now() if abs((now - date).total_seconds()) > 10: return redirect(service.main_url) sm = ServiceMap.objects.filter(user=request.user, service=service).first() if not sm: return redirect(service.main_url) m = hmac.new(str(service.secret_key), str('%s:%s' % (time, sm.sid))).hexdigest() m_client = request.GET.get('m', '') if constant_time_compare(m, m_client): logger.info('logout', {'r': request}) auth.logout(request) return redirect(service.main_url)
def check_password(raw_password, enc_password): """ Returns a boolean of whether the raw_password was correct. Handles encryption formats behind the scenes. """ algo, salt, hsh = enc_password.split('$') return constant_time_compare(hsh, get_hexdigest(algo, salt, raw_password))
def authenticate_credentials(self, payload): """ Return a non-deleted user that matches the payload's user id. Mimic what our UserAndAddrMiddleware and django's get_user() do when authenticating, because otherwise that behaviour would be missing in the API since API auth happens after the middleware process request phase. """ if 'user_id' not in payload: log.info('No user_id in token payload {}'.format(payload)) raise exceptions.AuthenticationFailed() try: user = UserProfile.objects.filter(deleted=False).get( pk=payload['user_id']) except UserProfile.DoesNotExist: log.info('User not found from token payload {}'.format(payload)) raise exceptions.AuthenticationFailed() # Check get_session_auth_hash like django's get_user() does. session_auth_hash = user.get_session_auth_hash() payload_auth_hash = payload.get('auth_hash', '') if not constant_time_compare(payload_auth_hash, session_auth_hash): log.info('User tried to authenticate with invalid auth hash in' 'payload {}'.format(payload)) raise exceptions.AuthenticationFailed() # Set user in thread like UserAndAddrMiddleware does. core.set_user(user) return user
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)
async def get_user(scope: Dict[str, Any]) -> Dict[str, Any]: """ Returns a user id from a channels-scope-session. If no user is retrieved, return {'id': 0}. """ # This code is basicly from channels.auth: # https://github.com/django/channels/blob/d5e81a78e96770127da79248349808b6ee6ec2a7/channels/auth.py#L16 if "session" not in scope: raise ValueError( "Cannot find session in scope. You should wrap your consumer in SessionMiddleware." ) session = scope["session"] user: Optional[Dict[str, Any]] = None try: user_id = _get_user_session_key(session) backend_path = session[BACKEND_SESSION_KEY] except KeyError: pass else: if backend_path in settings.AUTHENTICATION_BACKENDS: user = await element_cache.get_element_full_data("users/user", user_id) if user: # Verify the session session_hash = session.get(HASH_SESSION_KEY) session_hash_verified = session_hash and constant_time_compare( session_hash, user["session_auth_hash"] ) if not session_hash_verified: session.flush() user = None return user or {"id": 0}
def validate_request(request): """Check an incoming request. Returns: - True if authentication passed - Adding request['REMOTE_USER'] as authenticated username. """ if getattr(settings, 'BASICAUTH_DISABLE', False): # Not to use this env return True if 'HTTP_AUTHORIZATION' not in request.META: return False authorization_header = request.META['HTTP_AUTHORIZATION'] ret = extract_basicauth(authorization_header) if not ret: return False username, password = ret raw_pass = settings.BASICAUTH_USERS.get(username) if raw_pass is None: return False # To avoid timing atacks # https://security.stackexchange.com/questions/83660/simple-string-comparisons-not-secure-against-timing-attacks if not constant_time_compare(raw_pass, password): return False request.META['REMOTE_USER'] = username return True
def _decode_old(self, session_data): encoded_data = base64.decodestring(session_data) pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] if not constant_time_compare(md5_constructor(pickled + settings.SECRET_KEY).hexdigest(), tamper_check): raise SuspiciousOperation("User tampered with session cookie.") return pickle.loads(pickled)
def check_token(self, user, token): """ Check that a password reset token is correct for a given user. """ # 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 if (self._num_days(self._today()) - ts) > REGISTRATION_TIMEOUT_DAYS: return False return True
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), )
def check_token(self, newsletter_recipient, token): """ Check that a newsletter unsubscribe token is correct for a given recipient. """ # 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( newsletter_recipient, ts ), token ): return False return True
def get_user(scope): """ Return the user model instance associated with the given scope. If no user is retrieved, return an instance of `AnonymousUser`. """ if "session" not in scope: raise ValueError( "Cannot find session in scope. You should wrap your consumer in SessionMiddleware." ) session = scope["session"] user = None try: user_id = _get_user_session_key(session) backend_path = 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 = 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: session.flush() user = None return user or AnonymousUser()
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
def _check_security_hash(self, token, request, form): expected = self.security_hash(request, form) if constant_time_compare(token, expected): return True else: raise return False
def check_token(self, instance, token): """ Check that a token is correct for a given instance. """ # Parse the token try: nd_b36, hash = token.split("-") except ValueError: return False try: nd = base36_to_int(nd_b36) except ValueError: return False # Check that the num_days/uid has not been tampered with if not constant_time_compare(self._make_token_with_timestamp(instance, nd), token): return False # Check the num_days is within limit if (self._num_days(self._today()) - nd) > self._timeout_days(): return False return True
def get_express_session(req, cookie_name='express_sess'): """ get the Express.js session dict. default session cookie name of express:sess does now work with django. """ key = settings.SECRET_KEY cookie_value = req.COOKIES.get(cookie_name) cookie_sig = req.COOKIES.get(cookie_name + '.sig') if not cookie_value or not cookie_sig: return {} cookie = cookie_name + '=' + cookie_value hmac = Hmac.new(key, cookie, hashlib.sha1) digest = signing.b64_encode(hmac.digest()) valid_sig = crypto.constant_time_compare(digest, cookie_sig) if not valid_sig: return {} try: json_data = signing.b64_decode(cookie_value) session = json.loads(json_data) except: return {} return session
def check_token(self, user, token, token_expires=True): """ Check that a password reset token is correct for a given user. """ # 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 token_is_expired = all( token_expires, (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS, ) if token_is_expired: return False return True
def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" if self.data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) if self.FORCE_STATE_CHECK: if 'state' not in self.data: raise AuthMissingParameter(self, 'state') state = self.request.session[self.AUTH_BACKEND.name + '_state'] if not constant_time_compare(self.data['state'], state): raise AuthForbidden(self) client_id, client_secret = self.get_key_and_secret() params = {'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.redirect_uri} headers = {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'} request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=headers) try: response = simplejson.loads(urlopen(request).read()) except HTTPError, e: if e.code == 400: raise AuthCanceled(self) else: raise
def health(request): secret = settings.HEALTH_SECRET if secret is not None and not request.user.is_superuser: token = request.META.get('HTTP_X_TOKEN', None) if token is None or not constant_time_compare(token, secret): raise PermissionDenied() conn = get_redis_connection() workers = Worker.all(connection=conn) queues = defaultdict(lambda: defaultdict(int)) for worker in workers: for queue in worker.queues: queues[queue.name]['workers'] += 1 queues[queue.name]['tasks'] = queue.count data = { 'queues': queues, 'users': { 'total': User.objects.all().count(), 'active': User.objects.filter(is_suspended=False).count(), }, 'feeds': { 'total': Feed.objects.all().count(), 'unique': UniqueFeed.objects.all().count(), }, } response = HttpResponse(json.dumps(data)) response['Content-Type'] = 'application/json' return response
def _test_signature(self, request): """ Return True/False if the signature is recognized Note, we accept from UI server, admin server and MI server. Note, we set the `server_name` attribute of the matched server on request for permission management. """ offered = request.META.get("HTTP_X_SIGNATURE") if not offered: return False # check each server secret for a match servers = [ (settings.UI_SECRET, 'ui'), (settings.ADMIN_SECRET, 'admin'), (settings.MI_SECRET, 'mi'), (settings.DATA_SECRET, 'data') ] for secret, server_name in servers: generated = self._generate_signature( secret, request.get_full_path(), request.body, ) if constant_time_compare(generated, offered): request.server_name = server_name return True
def get_user(request): """ Returns the user model instance associated with the given request session. If no user is retrieved an instance of `MojAnonymousUser` is returned. """ user = None try: user_id = request.session[SESSION_KEY] token = request.session[AUTH_TOKEN_SESSION_KEY] user_data = request.session[USER_DATA_SESSION_KEY] 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, token, user_data) # 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 MojAnonymousUser()
def _broadcast(self, model, pk, action): signature = self.request.headers.get('X-Signature', None) if not signature: raise HTTPError(400) try: result = self.application.signer.unsign(signature, max_age=60 * 1) except (BadSignature, SignatureExpired): raise HTTPError(400) else: expected = '{method}:{url}:{body}'.format( method=self.request.method.lower(), url=self.request.full_url(), body=hashlib.sha256(self.request.body).hexdigest(), ) if not constant_time_compare(result, expected): raise HTTPError(400) try: body = json.loads(self.request.body.decode('utf-8')) except ValueError: body = None message = json.dumps({ 'model': model, 'id': pk, 'action': action, 'body': body, }) self.application.broadcast(message) self.write("Ok")
def check_http_auth(request): """ Check if a request includes HTTP authentication. If HTTP authentication is permitted for the given request, and a valid username and password are provided, set request.user to the corresponding user object. Otherwise, the request is not modified. For safety, HTTP authentication is only used for certain requests from non-interactive user agents; see http_auth_allowed(). This should be invoked at the start of the view before checking user credentials, and should be paired with require_http_auth(). """ if 'HTTP_AUTHORIZATION' in request.META: # If an Authorization header is supplied, but this request is # not allowed to use HTTP authentication, ignore the header. if not http_auth_allowed(request): return # If the user is already authenticated, ignore the header. if request.user.is_authenticated: return try: uid = request.session['pn_httpauth_uid'] authhash = request.session['pn_httpauth_hash'] user = User.objects.get(id=uid) except (KeyError, User.DoesNotExist): pass else: # Existing session is valid only if the password has not # changed. if constant_time_compare(user.get_session_auth_hash(), authhash) and user.is_active: request.user = user return tokens = request.META['HTTP_AUTHORIZATION'].split() if len(tokens) == 2 and tokens[0].lower() == 'basic': try: data = base64.b64decode(tokens[1], validate=True).decode() username, password = data.split(':', 1) except Exception: return user = auth.authenticate(request=request, username=username, password=password) if user and user.is_active: request.user = user # If the client supports cookies, save the state so # that we don't have to verify the password on # subsequent requests. If the client doesn't support # cookies, don't bother. if request.COOKIES: # We don't invoke auth.login() here, specifically # so that this session ID cannot be reused to # access URLs that don't permit HTTP # authentication. request.session['pn_httpauth_uid'] = user.id request.session['pn_httpauth_hash'] \ = user.get_session_auth_hash()
def test_constant_time_compare(self): # It's hard to test for constant time, just test the result. self.assertTrue(constant_time_compare(b'spam', b'spam')) self.assertFalse(constant_time_compare(b'spam', b'eggs')) self.assertTrue(constant_time_compare('spam', 'spam')) self.assertFalse(constant_time_compare('spam', 'eggs'))
def verify(self, password, encoded): encoded_2 = self.encode(password, '') return constant_time_compare(encoded, encoded_2)
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)
def check_and_unfollow(self, check): secret = self.get_follow_secret() if constant_time_compare(check, secret): self.delete() return True return False
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None try: csrf_token = _sanitize_token( request.COOKIES[settings.CSRF_COOKIE_NAME]) # Use same token next time request.META['CSRF_COOKIE'] = csrf_token except KeyError: csrf_token = None # Generate token and store it in the request, so it's # available to the view. request.META["CSRF_COOKIE"] = _get_new_csrf_key() # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC2616 needs protection if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. # It comes after the creation of CSRF cookies, so that # everything else continues to work exactly the same # (e.g. cookies are sent, etc.), but before any # branches that call reject(). return self._accept(request) if request.is_secure(): # Suppose user visits http://example.com/ # An active network attacker (man-in-the-middle, MITM) sends a # POST form that targets https://example.com/detonate-bomb/ and # submits it via JavaScript. # # The attacker will need to provide a CSRF cookie and token, but # that's no problem for a MITM and the session-independent # nonce we're using. So the MITM can circumvent the CSRF # protection. This is true for any HTTP connection, but anyone # using HTTPS expects better! For this reason, for # https://example.com/ we need additional protection that treats # http://example.com/ as completely untrusted. Under HTTPS, # Barth et al. found that the Referer header is missing for # same-domain requests in only about 0.2% of cases or less, so # we can use strict Referer checking. referer = request.META.get('HTTP_REFERER') if referer is None: return self._reject(request, REASON_NO_REFERER) # Note that request.get_host() includes the port. good_referer = 'https://%s/' % request.get_host() if not same_origin(referer, good_referer): reason = REASON_BAD_REFERER % (referer, good_referer) return self._reject(request, reason) if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. return self._reject(request, REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. request_csrf_token = "" if request.method == "POST": request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') if not constant_time_compare(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def verify(self, password, encoded): crypt = self._load_library() decoded = self.decode(encoded) data = crypt.crypt(password, decoded['hash']) return constant_time_compare(decoded['hash'], data)
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)
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)
def verify(self, password, encoded): decoded = self.decode(encoded) encoded_2 = self.encode(password, decoded['salt']) return constant_time_compare(encoded, encoded_2)
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(force_str(password), data))
def verify(self, password, encoded): decoded = self.decode(encoded) encoded_2 = self.encode(password, decoded["salt"], decoded["iterations"]) return constant_time_compare(encoded, encoded_2)
def check_password(self, raw_password): hasher = sha256() raw_password = raw_password + '_' + self.salt hasher.update(raw_password.encode('utf-8')) result = constant_time_compare(hasher.hexdigest(), self.password) return result
def post(self, request): grant_type = request.POST.get('grant_type') if grant_type == 'authorization_code': client_id = request.POST.get('client_id') client_secret = request.POST.get('client_secret') redirect_uri = request.POST.get('redirect_uri') code = request.POST.get('code') if not client_id: return self.error('invalid_client') if not client_secret: return self.error('invalid_client') try: application = ApiApplication.objects.get( client_id=client_id, status=ApiApplicationStatus.active, ) except ApiApplication.DoesNotExist: return self.error('invalid_client') if not constant_time_compare(client_secret, application.client_secret): return self.error('invalid_client') try: grant = ApiGrant.objects.get(application=application, code=code) except ApiGrant.DoesNotExist: return self.error('invalid_grant') if grant.is_expired(): return self.error('invalid_grant') if not redirect_uri: redirect_uri = application.get_default_redirect_uri() elif grant.redirect_uri != redirect_uri: return self.error('invalid_grant') token = ApiToken.from_grant(grant) elif grant_type == 'refresh_token': refresh_token = request.POST.get('refresh_token') scope = request.POST.get('scope') client_id = request.POST.get('client_id') client_secret = request.POST.get('client_secret') if not refresh_token: return self.error('invalid_request') # TODO(dcramer): support scope if scope: return self.error('invalid_request') if not client_id: return self.error('invalid_client') if not client_secret: return self.error('invalid_client') try: application = ApiApplication.objects.get( client_id=client_id, status=ApiApplicationStatus.active, ) except ApiApplication.DoesNotExist: return self.error('invalid_client') if not constant_time_compare(client_secret, application.client_secret): return self.error('invalid_client') try: token = ApiToken.objects.get( application=application, refresh_token=refresh_token, ) except ApiToken.DoesNotExist: return self.error('invalid_grant') token.refresh() else: return self.error('unsupported_grant_type') return HttpResponse(json.dumps({ 'access_token': token.token, 'refresh_token': token.refresh_token, 'expires_in': (timezone.now() - token.expires_at).total_seconds(), 'expires_at': token.expires_at, 'token_type': 'bearer', 'scope': ' '.join(token.get_scopes()), # NOQA 'user': { 'id': six.text_type(token.user.id), # we might need these to become scope based 'name': token.user.name, 'email': token.user.email, }, }), content_type='application/json')
def constant_time_compare(val1, val2): """Performs constant_time_compare with consistent typing""" return crypto.constant_time_compare(binary_type(val1), binary_type(val2))
def _check_security_hash(self, token, request, form): expected = self.security_hash(request, form) return constant_time_compare(token, expected)
def from_request(cls, request, token): """Returns a system token if this is a valid system request.""" system_token = get_system_token() if constant_time_compare(system_token, token) and is_internal_ip(request): return cls() return None
def check_token(self, user, token): return constant_time_compare(token, self._make_token(user))
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None try: csrf_token = _sanitize_token( request.COOKIES[settings.CSRF_COOKIE_NAME]) # Use same token next time request.META['CSRF_COOKIE'] = csrf_token except KeyError: csrf_token = None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC2616 needs protection if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. # It comes after the creation of CSRF cookies, so that # everything else continues to work exactly the same # (e.g. cookies are sent, etc.), but before any # branches that call reject(). return self._accept(request) if request.is_secure(): # Suppose user visits http://example.com/ # An active network attacker (man-in-the-middle, MITM) sends a # POST form that targets https://example.com/detonate-bomb/ and # submits it via JavaScript. # # The attacker will need to provide a CSRF cookie and token, but # that's no problem for a MITM and the session-independent # nonce we're using. So the MITM can circumvent the CSRF # protection. This is true for any HTTP connection, but anyone # using HTTPS expects better! For this reason, for # https://example.com/ we need additional protection that treats # http://example.com/ as completely untrusted. Under HTTPS, # Barth et al. found that the Referer header is missing for # same-domain requests in only about 0.2% of cases or less, so # we can use strict Referer checking. referer = force_text( request.META.get('HTTP_REFERER'), strings_only=True, errors='replace' ) if referer is None: return self._reject(request, REASON_NO_REFERER) referer = urlparse(referer) # Make sure we have a valid URL for Referer. if '' in (referer.scheme, referer.netloc): return self._reject(request, REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. if referer.scheme != 'https': return self._reject(request, REASON_INSECURE_REFERER) # If there isn't a CSRF_COOKIE_DOMAIN, assume we need an exact # match on host:port. If not, obey the cookie rules. if settings.CSRF_COOKIE_DOMAIN is None: # request.get_host() includes the port. good_referer = request.get_host() else: good_referer = settings.CSRF_COOKIE_DOMAIN server_port = request.META['SERVER_PORT'] if server_port not in ('443', '80'): good_referer = '%s:%s' % (good_referer, server_port) # Here we generate a list of all acceptable HTTP referers, # including the current host since that has been validated # upstream. good_hosts = list(settings.CSRF_TRUSTED_ORIGINS) good_hosts.append(good_referer) if not any(is_same_domain(referer.netloc, host) for host in good_hosts): reason = REASON_BAD_REFERER % referer.geturl() return self._reject(request, reason) if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. return self._reject(request, REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') except IOError: # Handle a broken connection before we've completed reading # the POST data. process_view shouldn't raise any # exceptions, so we'll ignore and serve the user a 403 # (assuming they're still listening, which they probably # aren't because of the error). pass if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') if not constant_time_compare(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def process_view(self, request, callback, callback_args, callback_kwargs): # 如果请求里面设置了csrf_done,则没有任何处理,一旦这个设置了,那么整个中间件都 # 不会产生作用 if getattr(request, 'csrf_processing_done', False): return None try: # 从cookie里面拿出之前设置的cookie,将其净化 csrf_token = _sanitize_token( request.COOKIES[settings.CSRF_COOKIE_NAME]) # Use same token next time request.META['CSRF_COOKIE'] = csrf_token # 没有设置过,则在request中设置一个CSRF_COOKIE,这次请求必须是一个GET请求. # 如果开发的网站使用GET,HEAD等请求更新数据库(不符合规范),那么CSRF中间层不会 # 正确的验证,因此绝对不要这样. except KeyError: csrf_token = None # Generate token and store it in the request, so it's # available to the view. request.META["CSRF_COOKIE"] = _get_new_csrf_key() # TODO:设置了就跳过这里的处理,下面的注释的意思 # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC2616 needs protection if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): # 如果请求中设置了不要开启csrf检查则直接ok,应该是其他中间件可以控制的,如 # 果这个设置了,中间件只是处理response验证.以及设置个Cookie if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. # It comes after the creation of CSRF cookies, so that # everything else continues to work exactly the same # (e.g. cookies are sent, etc.), but before any # branches that call reject(). return self._accept(request) # 如果使用HTTPS,那么必须设置了refer并且和目标HOST一致. if request.is_secure(): # Suppose user visits http://example.com/ # An active network attacker (man-in-the-middle, MITM) sends a # POST form that targets https://example.com/detonate-bomb/ and # submits it via JavaScript. # # The attacker will need to provide a CSRF cookie and token, but # that's no problem for a MITM and the session-independent # nonce we're using. So the MITM can circumvent the CSRF # protection. This is true for any HTTP connection, but anyone # using HTTPS expects better! For this reason, for # https://example.com/ we need additional protection that treats # http://example.com/ as completely untrusted. Under HTTPS, # Barth et al. found that the Referer header is missing for # same-domain requests in only about 0.2% of cases or less, so # we can use strict Referer checking. referer = force_text(request.META.get('HTTP_REFERER'), strings_only=True, errors='replace') if referer is None: return self._reject(request, REASON_NO_REFERER) # Note that request.get_host() includes the port. good_referer = 'https://%s/' % request.get_host() if not same_origin(referer, good_referer): reason = REASON_BAD_REFERER % (referer, good_referer) return self._reject(request, reason) # 没有csrf_token则拒绝,也就是说对网站的第一个请求必须是get,因为之后会设置csrf. if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. return self._reject(request, REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. # 拿到其中的另一个csrf设置,对于POST,是csrfmt,对于其他,是HTTP_X_CSRFTOKEN # 判断这两个和POST中的是否一致. 可以POST.因为存在cookie中,而其他程序拿不到cookie值 # 能发送但是不能拿到,然后在加上另外的值其他程序就没办法了 request_csrf_token = "" if request.method == "POST": try: # 是Django模板系统设置的. request_csrf_token = request.POST.get( 'csrfmiddlewaretoken', '') except IOError: # Handle a broken connection before we've completed reading # the POST data. process_view shouldn't raise any # exceptions, so we'll ignore and serve the user a 403 # (assuming they're still listening, which they probably # aren't because of the error). pass if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. # TODO: 是哪里设置的?:视图,模板等设置的. request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') if not constant_time_compare(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def check_token(self, token, path): return constant_time_compare( token, salted_hmac(self.key_salt, path).hexdigest()[::2])
def verify(self, password, encoded): algorithm, data = encoded.split('$', 1) assert algorithm == self.algorithm bcrypt = self._load_library() return constant_time_compare(data, bcrypt.hashpw(password, data))
def check_confirmation_secret(self, secret, *args): return constant_time_compare(secret, self.generate_confirmation_secret(*args))
def check_autologin_secret(self, secret): return constant_time_compare(self.generate_autologin_secret(), secret)
def verify_project_token(cls, token, project_slug): expected_token = cls.get_project_token(project_slug) return constant_time_compare(token, expected_token)
def has_permission(self, request, view): return constant_time_compare( request.GET.get('token'), settings.CSV_DUMP_AUTH_TOKEN )
def verify(self, payload, key, signature): return constant_time_compare( signature, hmac.new(key=key.encode("utf-8"), msg=payload, digestmod=sha256).hexdigest(), )
def get_session_data(self, current_datetime=None): """ Return the current session data, with native types coerced. """ request = self.request data = request.session.get(SESSION_KEY) try: cookie_token = request.get_signed_cookie( key=COOKIE_NAME, default=None, salt=COOKIE_SALT, max_age=MAX_AGE.total_seconds()) except BadSignature: logger.exception( "superuser.bad-cookie-signature", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return if not cookie_token: if data: logger.warn( "superuser.missing-cookie-token", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return False elif not data: logger.warn( "superuser.missing-session-data", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return session_token = data.get("tok") if not session_token: logger.warn( "superuser.missing-session-token", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return if not constant_time_compare(cookie_token, session_token): logger.warn( "superuser.invalid-token", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return if data["uid"] != six.text_type(request.user.id): logger.warn( "superuser.invalid-uid", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id, "expected_user_id": data["uid"], }, ) return if current_datetime is None: current_datetime = timezone.now() try: data["idl"] = datetime.utcfromtimestamp(float( data["idl"])).replace(tzinfo=timezone.utc) except (TypeError, ValueError): logger.warn( "superuser.invalid-idle-expiration", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, exc_info=True, ) return if data["idl"] < current_datetime: logger.info( "superuser.session-expired", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return try: data["exp"] = datetime.utcfromtimestamp(float( data["exp"])).replace(tzinfo=timezone.utc) except (TypeError, ValueError): logger.warn( "superuser.invalid-expiration", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, exc_info=True, ) return if data["exp"] < current_datetime: logger.info( "superuser.session-expired", extra={ "ip_address": request.META["REMOTE_ADDR"], "user_id": request.user.id }, ) return return data
def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None # If the user doesn't have a CSRF cookie, generate one and store it in the # request, so it's available to the view. We'll store it in a cookie when # we reach the response. try: # In case of cookies from untrusted sources, we strip anything # dangerous at this point, so that the cookie + token will have the # same, sanitized value. request.META["CSRF_COOKIE"] = _sanitize_token( request.COOKIES[settings.CSRF_COOKIE_NAME]) cookie_is_new = False except KeyError: # No cookie, so create one. This will be sent with the next # response. request.META["CSRF_COOKIE"] = _get_new_csrf_key() # Set a flag to allow us to fall back and allow the session id in # place of a CSRF cookie for this request only. cookie_is_new = True # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None if request.method == 'POST': if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. It comes after # the creation of CSRF cookies, so that everything else continues to # work exactly the same (e.g. cookies are sent etc), but before the # any branches that call reject() return self._accept(request) if request.is_ajax(): # .is_ajax() is based on the presence of X-Requested-With. In # the context of a browser, this can only be sent if using # XmlHttpRequest. Browsers implement careful policies for # XmlHttpRequest: # # * Normally, only same-domain requests are allowed. # # * Some browsers (e.g. Firefox 3.5 and later) relax this # carefully: # # * if it is a 'simple' GET or POST request (which can # include no custom headers), it is allowed to be cross # domain. These requests will not be recognized as AJAX. # # * if a 'preflight' check with the server confirms that the # server is expecting and allows the request, cross domain # requests even with custom headers are allowed. These # requests will be recognized as AJAX, but can only get # through when the developer has specifically opted in to # allowing the cross-domain POST request. # # So in all cases, it is safe to allow these requests through. return self._accept(request) if request.is_secure(): # Suppose user visits http://example.com/ # An active network attacker,(man-in-the-middle, MITM) sends a # POST form which targets https://example.com/detonate-bomb/ and # submits it via javascript. # # The attacker will need to provide a CSRF cookie and token, but # that is no problem for a MITM and the session independent # nonce we are using. So the MITM can circumvent the CSRF # protection. This is true for any HTTP connection, but anyone # using HTTPS expects better! For this reason, for # https://example.com/ we need additional protection that treats # http://example.com/ as completely untrusted. Under HTTPS, # Barth et al. found that the Referer header is missing for # same-domain requests in only about 0.2% of cases or less, so # we can use strict Referer checking. referer = request.META.get('HTTP_REFERER') if referer is None: logger.warning('Forbidden (%s): %s' % (REASON_NO_COOKIE, request.path), extra={ 'status_code': 403, 'request': request, }) return self._reject(request, REASON_NO_REFERER) # The following check ensures that the referer is HTTPS, # the domains match and the ports match - the same origin policy. good_referer = 'https://%s/' % request.get_host() if not referer.startswith(good_referer): reason = REASON_BAD_REFERER % (referer, good_referer) logger.warning('Forbidden (%s): %s' % (reason, request.path), extra={ 'status_code': 403, 'request': request, }) return self._reject(request, reason) # If the user didn't already have a CSRF cookie, then fall back to # the Django 1.1 method (hash of session ID), so a request is not # rejected if the form was sent to the user before upgrading to the # Django 1.2 method (session independent nonce) if cookie_is_new: try: session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] csrf_token = _make_legacy_session_token(session_id) except KeyError: # No CSRF cookie and no session cookie. For POST requests, # we insist on a CSRF cookie, and in this way we can avoid # all CSRF attacks, including login CSRF. logger.warning('Forbidden (%s): %s' % (REASON_NO_COOKIE, request.path), extra={ 'status_code': 403, 'request': request, }) return self._reject(request, REASON_NO_COOKIE) else: csrf_token = request.META["CSRF_COOKIE"] # check incoming token request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') if not constant_time_compare(request_csrf_token, csrf_token): if cookie_is_new: # probably a problem setting the CSRF cookie logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path), extra={ 'status_code': 403, 'request': request, }) return self._reject(request, REASON_NO_CSRF_COOKIE) else: logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path), extra={ 'status_code': 403, 'request': request, }) return self._reject(request, REASON_BAD_TOKEN) return self._accept(request)
def get_email(self): expected_digest = salted_hmac(BLACKLIST_HMAC_SALT, self.kwargs['email']) if not constant_time_compare(expected_digest.hexdigest(), self.kwargs['digest']): raise Http404 return self.kwargs['email']