def verify_signature(request, key_id, headers, signature): # Fetch the key req = requests.get(key_id, headers={"Accept": "application/ld+json"}) pubkey = req.json()["publicKey"]["publicKeyPem"] #verify digest = request.META.get("HTTP_DIGEST", None) request_headers = { "signature": signature, "host": request.get_host(), "date": request.META['HTTP_DATE'], "content-type": request.META["CONTENT_TYPE"] } if digest: new_digest = hashlib.sha256() new_digest.update(request.body) new_digest = base64.encodestring(new_digest.digest()).strip() request_headers["digest"] = (b"SHA-256=" + new_digest).decode("UTF-8") # TODO: check date stamp to avoid replay attacks # TODO: check attribution hv = HeaderVerifier(request_headers, pubkey, headers, request.method, request.path, sign_header='Signature') return hv.verify()
def authenticate(self, request): """ Perform the actual authentication. Note that the exception raised is always the same. This is so that we don't leak information about in/valid keyIds and other such useful things. """ auth_header = authentication.get_authorization_header(request) if not auth_header or len(auth_header) == 0: return None method, fields = utils.parse_authorization_header(auth_header) # Ignore foreign Authorization headers. if method.lower() != 'signature': return None # Verify basic header structure. if len(fields) == 0: raise FAILED # Ensure all required fields were included. if len(set(("keyid","algorithm","signature")) - set(fields.keys())) > 0: raise FAILED # Fetch the secret associated with the keyid user, secret = self.fetch_user_data( fields["keyid"], algorithm=fields["algorithm"] ) if not (user and secret): raise FAILED # Gather all request headers and translate them as stated in the Django docs: # https://docs.djangoproject.com/en/1.6/ref/request-response/#django.http.HttpRequest.META headers = {} for key in request.META.keys(): if key.startswith("HTTP_") or \ key in ("CONTENT_TYPE", "CONTENT_LENGTH"): header = key[5:].lower().replace('_', '-') headers[header] = request.META[key] # Verify headers hs = HeaderVerifier( headers, secret, required_headers=self.required_headers, method=request.method.lower(), path=request.get_full_path() ) # All of that just to get to this. if not hs.verify(): raise FAILED return (user, fields["keyid"])
def authenticate(self, request): """ Perform the actual authentication. Note that the exception raised is always the same. This is so that we don't leak information about in/valid keyIds and other such useful things. """ auth_header = authentication.get_authorization_header(request) if not auth_header or len(auth_header) == 0: return None method, fields = utils.parse_authorization_header(auth_header) # Ignore foreign Authorization headers. if method.lower() != 'signature': return None # Verify basic header structure. if len(fields) == 0: raise FAILED # Ensure all required fields were included. if len(set(("keyid", "algorithm", "signature")) - set(fields.keys())) > 0: raise FAILED # Fetch the secret associated with the keyid try: user, secret = self.fetch_user_data(fields['keyid'], algorithm=fields["algorithm"]) except Exception as ex: print('Received Exception: {}'.format(ex)) if not (user and secret): raise FAILED # Gather all request headers and translate them as stated in the Django docs: # https://docs.djangoproject.com/en/1.6/ref/request-response/#django.http.HttpRequest.META headers = {} for key in request.META.keys(): if key.startswith("HTTP_") or \ key in ("CONTENT_TYPE", "CONTENT_LENGTH"): header = key[5:].lower().replace('_', '-') headers[header] = request.META[key] # Verify headers hs = HeaderVerifier(headers, secret, required_headers=self.required_headers, method=request.method.lower(), path=request.get_full_path()) # All of that just to get to this. if not hs.verify(): raise FAILED return user, secret
def authenticate(self, request): # Check if request has a "Signature" request header. authorization_header = self.header_canonical('Authorization') auth_string = request.META.get(authorization_header) if not auth_string: raise exceptions.AuthenticationFailed('No signature provided') # Check for API key header. api_key = None if self.ALGORITHM.lower().startswith('rsa'): api_key = self.get_keyid_from_auth_string(auth_string) else: api_key_header = self.header_canonical(self.API_KEY_HEADER) api_key = request.META.get(api_key_header) if not api_key: raise exceptions.AuthenticationFailed('No api key provided') # Fetch credentials for API key from the data store. try: user, secret = self.fetch_user_data(api_key) except TypeError: raise exceptions.AuthenticationFailed('Bad API key') # Build string to sign from "headers" part of Signature value. path = request.get_full_path() sent_signature = request.META.get( self.header_canonical('Authorization')) host = request.META.get(self.header_canonical('Host')) signature_headers = self.get_headers_from_signature(sent_signature) unsigned = self.build_dict_to_sign(request, signature_headers) unsigned.update({'authorization': auth_string}) #unsigned['date'] = unsigned['date'] + 'd' try: hv = HeaderVerifier(headers=unsigned, secret=secret, required_headers=self.REQUIRED_HEADERS, method=request.method, path=path, host=host) except (HttpSigException, KeyError, Exception) as e: raise exceptions.AuthenticationFailed(str(e)) try: if not hv.verify(): raise exceptions.AuthenticationFailed('Bad signature') except Exception as e: raise exceptions.AuthenticationFailed(str(e)) return (user, api_key)
def is_signature_valid(self, site, request): """Return whether the request signature is valid for ``site``.""" secret = site.private_key if site.is_key_algo_symmetric() else site.public_key x = _header_canonical("Authorization") sent_signature = request.META.get(x) signature_headers = self.get_headers_from_signature(sent_signature) if len({"date", "x-beacon-user"} & set(signature_headers)) != 2: raise exceptions.AuthenticationFailed("Headers Date and X-Beacon-User must be signed") if "authorization" not in signature_headers: signature_headers += ["authorization"] headers_to_sign = self.build_dict_to_verify(request, signature_headers) # Sign string and compare. verifier = HeaderVerifier(headers=headers_to_sign, secret=secret) return verifier.verify()
def authenticate(self, request): """ Perform the actual authentication. Note that the exception raised is always the same. This is so that we don't leak information about in/valid keyIds and other such useful things. """ auth_header = authentication.get_authorization_header(request) if not auth_header or len(auth_header) == 0: return None method, fields = utils.parse_authorization_header(auth_header) # Ignore foreign Authorization headers. if method.lower() != "signature": return None # Verify basic header structure. if len(fields) == 0: raise FAILED # Ensure all required fields were included. if len(set(("keyid", "algorithm", "signature")) - set(fields.keys())) > 0: raise FAILED # Fetch the secret associated with the keyid user, secret = self.fetch_user_data(fields["keyid"], algorithm=fields["algorithm"]) if not (user and secret): raise FAILED # Verify headers hs = HeaderVerifier( request.headers, secret, required_headers=self.required_headers, method=request.method.lower(), path=request.get_full_path(), ) # All of that just to get to this. if not hs.verify(): raise FAILED # Check if the signature is expired expires = request.headers.get("(expires)", None) try: expires = int(expires) except TypeError: expires = None if expires and time.time() > expires: raise FAILED if 'On-Behalf-Of' in request.headers: user = self.fetch_on_behalf_of_user( request.headers['On-Behalf-Of']) if not user: raise exceptions.AuthenticationFailed( "On behalf of user was not found.") return (user, None) return (user, fields["keyid"])