def _VerifyHMAC(self, comms=None): """Verifies the HMAC. This method raises a DecryptionError if the received HMAC does not verify. If the HMAC verifies correctly, True is returned. Args: comms: The comms RdfValue to verify. Raises: DecryptionError: The HMAC did not verify. Returns: True """ # Check the encrypted message integrity using HMAC. if self.hmac_type == "SIMPLE_HMAC": digest = self.HMAC(comms.encrypted) if not hmac.compare_digest(comms.hmac, digest): raise DecryptionError("HMAC verification failed.") elif self.hmac_type == "FULL_HMAC": digest = self.HMAC(comms.encrypted, comms.encrypted_cipher, comms.encrypted_cipher_metadata, comms.packet_iv.SerializeToString(), struct.pack("<I", comms.api_version)) if not hmac.compare_digest(comms.full_hmac, digest): raise DecryptionError("HMAC verification failed.") else: raise DecryptionError("HMAC type no supported.") return True
async def auth_middleware(request, handler): """Authenticate as middleware.""" # If no password set, just always set authenticated=True if api_password is None: request[KEY_AUTHENTICATED] = True return await handler(request) # Check authentication authenticated = False if (HTTP_HEADER_HA_AUTH in request.headers and hmac.compare_digest( api_password, request.headers[HTTP_HEADER_HA_AUTH])): # A valid auth header has been set authenticated = True elif (DATA_API_PASSWORD in request.query and hmac.compare_digest(api_password, request.query[DATA_API_PASSWORD])): authenticated = True elif (hdrs.AUTHORIZATION in request.headers and validate_authorization_header(api_password, request)): authenticated = True elif _is_trusted_ip(request, trusted_networks): authenticated = True request[KEY_AUTHENTICATED] = authenticated return await handler(request)
def serve_metrics(request): if not settings.METRICS_ENABLED: return unauthed_response() # check if the user is properly authorized: if "HTTP_AUTHORIZATION" not in request.META: return unauthed_response() method, credentials = request.META["HTTP_AUTHORIZATION"].split(" ", 1) if method.lower() != "basic": return unauthed_response() user, passphrase = base64.b64decode(credentials.strip()).decode().split(":", 1) if not hmac.compare_digest(user, settings.METRICS_USER): return unauthed_response() if not hmac.compare_digest(passphrase, settings.METRICS_PASSPHRASE): return unauthed_response() # ok, the request passed the authentication-barrier, let's hand out the metrics: m = metrics.metric_values() output = [] for metric, sub in m.items(): for label, value in sub.items(): output.append("{}{} {}".format(metric, label, str(value))) content = "\n".join(output) + "\n" return HttpResponse(content)
async def async_secure_get_client(hass, client_id, client_secret): """Get a client id/secret in consistent time.""" client = await hass.auth.async_get_client(client_id) if client is None: if client_secret is not None: # Still do a compare so we run same time as if a client was found. hmac.compare_digest(client_secret.encode('utf-8'), client_secret.encode('utf-8')) return None if client.secret is None: return client elif client_secret is None: # Still do a compare so we run same time as if a secret was passed. hmac.compare_digest(client.secret.encode('utf-8'), client.secret.encode('utf-8')) return None elif hmac.compare_digest(client_secret.encode('utf-8'), client.secret.encode('utf-8')): return client return None
async def auth_middleware(request, handler): """Authenticate as middleware.""" authenticated = False if use_auth and (HTTP_HEADER_HA_AUTH in request.headers or DATA_API_PASSWORD in request.query): if request.path not in old_auth_warning: _LOGGER.log( logging.INFO if support_legacy else logging.WARNING, 'You need to use a bearer token to access %s from %s', request.path, request[KEY_REAL_IP]) old_auth_warning.add(request.path) if (hdrs.AUTHORIZATION in request.headers and await async_validate_auth_header( request, api_password if legacy_auth else None)): # it included both use_auth and api_password Basic auth authenticated = True # We first start with a string check to avoid parsing query params # for every request. elif (request.method == "GET" and SIGN_QUERY_PARAM in request.query and await async_validate_signed_request(request)): authenticated = True elif (legacy_auth and HTTP_HEADER_HA_AUTH in request.headers and hmac.compare_digest( api_password.encode('utf-8'), request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))): # A valid auth header has been set authenticated = True request['hass_user'] = await legacy_api_password.async_get_user( app['hass']) elif (legacy_auth and DATA_API_PASSWORD in request.query and hmac.compare_digest( api_password.encode('utf-8'), request.query[DATA_API_PASSWORD].encode('utf-8'))): authenticated = True request['hass_user'] = await legacy_api_password.async_get_user( app['hass']) elif _is_trusted_ip(request, trusted_networks): users = await app['hass'].auth.async_get_users() for user in users: if user.is_owner: request['hass_user'] = user break authenticated = True elif not use_auth and api_password is None: # If neither password nor auth_providers set, # just always set authenticated=True authenticated = True request[KEY_AUTHENTICATED] = authenticated return await handler(request)
def handle_request(self, request, **values): """Handle request to url.""" from werkzeug.exceptions import MethodNotAllowed, Unauthorized if request.method == "OPTIONS": # For CORS preflight requests. return self.options(request) try: handler = getattr(self, request.method.lower()) except AttributeError: raise MethodNotAllowed # Auth code verbose on purpose authenticated = False if self.hass.wsgi.api_password is None: authenticated = True elif request.remote_addr in self.hass.wsgi.approved_ips: authenticated = True elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''), self.hass.wsgi.api_password): # A valid auth header has been set authenticated = True elif hmac.compare_digest(request.args.get(DATA_API_PASSWORD, ''), self.hass.wsgi.api_password): authenticated = True if self.requires_auth and not authenticated: _LOGGER.warning('Login attempt or request with an invalid ' 'password from %s', request.remote_addr) persistent_notification.create( self.hass, 'Invalid password used from {}'.format(request.remote_addr), 'Login attempt failed', NOTIFICATION_ID_LOGIN) raise Unauthorized() request.authenticated = authenticated _LOGGER.info('Serving %s to %s (auth: %s)', request.path, request.remote_addr, authenticated) result = handler(request, **values) if isinstance(result, self.Response): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result return self.Response(result, status=status_code)
def verify_token(token, secret, i=None, window_size=256): if i is None: return hmac.compare_digest(token, get_token(secret)) n = 0 for i in range(i, i + window_size): n += 1 if hmac.compare_digest(token, get_token(secret, i)): return n return False
def strings_equal(s1, s2): """ Timing-attack resistant string comparison. Normal comparison using == will short-circuit on the first mismatching character. This avoids that by scanning the whole string, though we still reveal to a timing attack whether the strings are the same length. """ try: # Python 3.3+ and 2.7.7+ include a timing-attack-resistant # comparison function, which is probably more reliable than ours. # Use it if available. from hmac import compare_digest return compare_digest(s1, s2) except ImportError: pass if len(s1) != len(s2): return False differences = 0 for c1, c2 in zip(s1, s2): differences |= ord(c1) ^ ord(c2) return differences == 0
def check_session_id_signature(session_id, secret_key=settings.secret_key_bytes(), signed=settings.sign_sessions()): """Check the signature of a session ID, returning True if it's valid. The server uses this function to check whether a session ID was generated with the correct secret key. If signed sessions are disabled, this function always returns True. Args: session_id (str) : The session ID to check secret_key (str, optional) : Secret key (default: value of 'BOKEH_SECRET_KEY' env var) signed (bool, optional) : Whether to check anything (default: value of 'BOKEH_SIGN_SESSIONS' env var) """ secret_key = _ensure_bytes(secret_key) if signed: pieces = session_id.split('-', 1) if len(pieces) != 2: return False base_id = pieces[0] provided_signature = pieces[1] expected_signature = _signature(base_id, secret_key) # hmac.compare_digest() uses a string compare algorithm that doesn't # short-circuit so we don't allow timing analysis # encode_utf8 is used to ensure that strings have same encoding return hmac.compare_digest(encode_utf8(expected_signature), encode_utf8(provided_signature)) else: return True
def _postreceive(self): """Callback from Flask""" digest = self._get_digest() if digest is not None: sig_parts = _get_header('X-Hub-Signature').split('=', 1) if (len(sig_parts) < 2 or sig_parts[0] != 'sha1' or not hmac.compare_digest(sig_parts[1], unicode(digest))): abort(400, 'Invalid signature') event_type = _get_header('X-Github-Event') data = request.get_json() if data is None: abort(400, 'Request body must contain json') self._logger.info( '%s (%s)', _format_event(event_type, data), _get_header('X-Github-Delivery')) for hook in self._hooks.get(event_type, []): hook(data) return '', 204
def strings_differ(string1, string2): """Check whether two strings differ while avoiding timing attacks. This function returns True if the given strings differ and False if they are equal. It's careful not to leak information about *where* they differ as a result of its running time, which can be very important to avoid certain timing-related crypto attacks: http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf """ len_eq = len(string1) == len(string2) if len_eq: invalid_bits = 0 left = string1 else: invalid_bits = 1 left = string2 right = string2 if compare_digest is not None: invalid_bits += not compare_digest(left, right) else: for a, b in zip(left, right): invalid_bits += a != b return invalid_bits != 0
def validate_token(self, user, token_hex): self._purge_old_tokens() possible_tokens = user.tokens for token in possible_tokens: if hmac.compare_digest(bytes(token_hex), bytes(token.value)): return True return False
def deserialize(self, msg_list, content=True, copy=True): """Unserialize a msg_list to a nested message dict. This is roughly the inverse of serialize. The serialize/deserialize methods work with full message lists, whereas pack/unpack work with the individual message parts in the message list. Parameters ---------- msg_list : list of bytes or Message objects The list of message parts of the form [HMAC,p_header,p_parent, p_metadata,p_content,buffer1,buffer2,...]. content : bool (True) Whether to unpack the content dict (True), or leave it packed (False). copy : bool (True) Whether msg_list contains bytes (True) or the non-copying Message objects in each place (False). Returns ------- msg : dict The nested message dict with top-level keys [header, parent_header, content, buffers]. The buffers are returned as memoryviews. """ minlen = 5 message = {} if not copy: # pyzmq didn't copy the first parts of the message, so we'll do it for i in range(minlen): msg_list[i] = msg_list[i].bytes if self.auth is not None: signature = msg_list[0] if not signature: raise ValueError("Unsigned Message") if signature in self.digest_history: raise ValueError("Duplicate Signature: %r" % signature) self._add_digest(signature) check = self.sign(msg_list[1:5]) if not compare_digest(signature, check): raise ValueError("Invalid Signature: %r" % signature) if not len(msg_list) >= minlen: raise TypeError("malformed message, must have at least %i elements"%minlen) header = self.unpack(msg_list[1]) message['header'] = extract_dates(header) message['msg_id'] = header['msg_id'] message['msg_type'] = header['msg_type'] message['parent_header'] = extract_dates(self.unpack(msg_list[2])) message['metadata'] = self.unpack(msg_list[3]) if content: message['content'] = self.unpack(msg_list[4]) else: message['content'] = msg_list[4] buffers = [memoryview(b) for b in msg_list[5:]] if buffers and buffers[0].shape is None: # force copy to workaround pyzmq #646 buffers = [memoryview(b.bytes) for b in msg_list[5:]] message['buffers'] = buffers # adapt to the current version return adapt(message)
def foo(): print "testing the webhook" sha_name, signature = request.headers.get('X-Hub-Signature').split('=') if sha_name != 'sha1': print "sha name doesnt equal sha1" mac = hmac.new(str('secret'), msg=request.data, digestmod=sha1) if (hmac.compare_digest(str(mac.hexdigest()), str(signature)) and request.headers.get('X-GitHub-Event') == u'push'): api_data = json.loads(request.data) print api_data print 'SECRET WAS A SUCCESS!' success_message = { 'status': 200, 'message': "Github Webhook: success!" } response = jsonify(success_message) response.status_code = 200 else: failure_message = { 'status': 400, 'message': "Github Webhook: failed!" } response = jsonify(failure_message) response.status_code = 400 return response
def __call__(self, environ, start_response): # Determine if the request comes from a trusted proxy or not by looking # for a token in the request. request_token = environ.get("HTTP_WAREHOUSE_TOKEN") if (request_token is not None and hmac.compare_digest(self.token, request_token)): # Compute our values from the environment. proto = environ.get("HTTP_WAREHOUSE_PROTO", "") remote_addr = environ.get("HTTP_WAREHOUSE_IP", "") host = environ.get("HTTP_WAREHOUSE_HOST", "") # Put the new header values into our environment. if remote_addr: environ["REMOTE_ADDR"] = remote_addr if host: environ["HTTP_HOST"] = host if proto: environ["wsgi.url_scheme"] = proto # Remove any of the forwarded or warehouse headers from the environment for header in { "HTTP_X_FORWARDED_PROTO", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_HOST", "HTTP_WAREHOUSE_TOKEN", "HTTP_WAREHOUSE_PROTO", "HTTP_WAREHOUSE_IP", "HTTP_WAREHOUSE_HOST"}: if header in environ: del environ[header] # Dispatch to the real underlying application. return self.app(environ, start_response)
def post(self, request, *args, **kwargs): secret = self.get_secret() if not secret: raise ImproperlyConfigured('GitHub webhook secret ist not defined.') if 'HTTP_X_HUB_SIGNATURE' not in request.META: return HttpResponseBadRequest('Request does not contain X-GITHUB-SIGNATURE header') if 'HTTP_X_GITHUB_EVENT' not in request.META: return HttpResponseBadRequest('Request does not contain X-GITHUB-EVENT header') digest_name, signature = request.META['HTTP_X_HUB_SIGNATURE'].split('=') if digest_name != 'sha1': return HttpResponseBadRequest('Unsupported X-HUB-SIGNATURE digest mode found: {}'.format(digest_name)) mac = hmac.new( secret.encode('utf-8'), msg=request.body, digestmod=hashlib.sha1 ) if not hmac.compare_digest(mac.hexdigest(), signature): return HttpResponseBadRequest('Invalid X-HUB-SIGNATURE header found') event = request.META['HTTP_X_GITHUB_EVENT'] if event not in self.get_allowed_events(): return HttpResponseBadRequest('Unsupported X-GITHUB-EVENT header found: {}'.format(event)) handler = getattr(self, event, None) if not handler: return HttpResponseBadRequest('Unsupported X-GITHUB-EVENT header found: {}'.format(event)) payload = json.loads(request.body.decode('utf-8')) response = handler(payload, request, *args, **kwargs) return JsonResponse(response)
def get_secure_cookie(self, key, max_time): ''' decode and verify a cookie set with :func:`Response.set_secure_cookie` :param key: ``key`` passed to ``set_secure_cookie`` :type max_time: `datetime.timedelta <https://docs.python.org/3/library/datetime.html#timedelta-objects>`_ or None :param max_time: amount of time since cookie was set that it should be considered valid for. this is normally equal to the ``max_age`` passed to ``set_secure_cookie``. longer times mean larger windows during which a replay attack is valid. this can be None, in which case no expiry check is performed :rtype: str or None ''' try: cookie = self.cookies[key].value except KeyError: return None try: value, ts, signature = cookie.rsplit('|', 2) ts = int(ts) except ValueError: raise exceptions.HTTPException(400, 'invalid %s cookie: %s' % (key, cookie)) value_ts = '%s|%d' % (value, int(ts)) if hmac.compare_digest(signature, _hash(key + '|' + value_ts, self.app.cookie_secret)): if max_time is not None and ts + max_time.total_seconds() < time.time(): # cookie has expired return None return value else: return None
def add_issue_bot(request): request_body = request.body.decode('utf-8') data = json.loads(request_body) mac = request.META.get('HTTP_X_MAC', None) computed_mac = hmac.new(settings.SHORA_SIGN_SECRET.encode('utf-8')) computed_mac.update(request_body.encode('utf-8')) if not mac or not hmac.compare_digest(mac, str(computed_mac.hexdigest())): logger.warning('HMAC authorization failed for bot. Header: ' + mac) return JsonResponse({ 'success': False, 'message': 'Not authorized' }) form = BotIssueForm(data=data) if form.is_valid(): Issue.objects.create( sender=None, title=form.cleaned_data['title'], category=30, category_optional=form.cleaned_data['place'], message=form.cleaned_data['description'], status=1, ) return JsonResponse({ 'success': True, 'message': 'Issue created' }) else: logger.error('Bot bad request') return JsonResponse({ 'success': False, 'message': 'Data format error in following fields: %s' % list(', '.join(form.errors)) })
def verify(self, password, encoded): algorithm, iterations, salt, hash = encoded.split('$', 3) if not algorithm == self.algorithm: return False encoded_2 = self.encode(password, salt, int(iterations)) return hmac.compare_digest(force_bytes(encoded), force_bytes(encoded_2))
def server_authenticate(connection, secret_key): message = os.urandom(32) connection.send(message) hash = hmac.new(secret_key, message) digest = hash.digest() response = connection.recv(len(digest)) return hmac.compare_digest(digest, response)
def verify_hmac_hash(data, signature): #GITHUB_TOKEN = settings.GITHUB_WEBHOOK_TOKEN path = "/.simpleHTTPServer_env" load_dotenv(path) GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") mac = hmac.new(GITHUB_TOKEN, msg=data, digestmod=hashlib.sha1) return hmac.compare_digest('sha1=' + mac.hexdigest(), signature)
def post(*arg, **kwarg): ''' This is the main listener for github webhooks. ''' # retrieve the headers from the request # headers = tangelo.request_headers() # <- not merged headers = cherrypy.request.headers # get the request body as a dict body = tangelo.request_body() s = body.read() # make sure this is a valid request coming from github computed_hash = hmac.new(str(_secret_key), s, hashlib.sha1).hexdigest() received_hash = headers.get('X-Hub-Signature', 'sha1=')[5:] if not hmac.compare_digest(computed_hash, received_hash): return tangelo.HTTPStatusCode(403, "Invalid signature") try: obj = json.loads(s) except: return tangelo.HTTPStatusCode(400, "Could not load json object.") if headers['X-Github-Event'] == 'push': # add a new item to the test queue add_push(obj) else: return tangelo.HTTPStatusCode(400, "Unhandled event") return 'OK'
def is_authenticated(self, user, password): """Validate credentials. Iterate through htpasswd credential file until user matches, extract hash (encrypted password) and check hash against user-given password, using the method specified in the Radicale config. The content of the file is not cached because reading is generally a very cheap operation, and it's useful to get live updates of the htpasswd file. """ try: with open(self.filename) as f: for line in f: line = line.rstrip("\n") if line.lstrip() and not line.lstrip().startswith("#"): try: login, hash_value = line.split(":", maxsplit=1) # Always compare both login and password to avoid # timing attacks, see #591. login_ok = hmac.compare_digest(login, user) password_ok = self.verify(hash_value, password) if login_ok and password_ok: return True except ValueError as e: raise RuntimeError("Invalid htpasswd file %r: %s" % (self.filename, e)) from e except OSError as e: raise RuntimeError("Failed to load htpasswd file %r: %s" % (self.filename, e)) from e return False
def decrypt(self, id, data): if not (data[0] == self.TYPE or data[0] == PassphraseKey.TYPE and isinstance(self, RepoKey)): raise IntegrityError('Invalid encryption envelope') hmac_given = memoryview(data)[1:33] hmac_computed = memoryview(HMAC(self.enc_hmac_key, memoryview(data)[33:], sha256).digest()) if not compare_digest(hmac_computed, hmac_given): raise IntegrityError('Encryption envelope checksum mismatch') self.dec_cipher.reset(iv=PREFIX + data[33:41]) data = self.compressor.decompress(self.dec_cipher.decrypt(data[41:])) if id: hmac_given = id hmac_computed = HMAC(self.id_key, data, sha256).digest() if not compare_digest(hmac_computed, hmac_given): raise IntegrityError('Chunk id verification failed') return data
def proxy_signature_is_valid(request, secret): """ Return true if the calculated signature matches that present in the query string of the given request. """ # Allow skipping of validation with an explicit setting. # If setting not present, skip if in debug mode by default. skip_validation = getattr(settings, 'SKIP_APP_PROXY_VALIDATION', settings.DEBUG) if skip_validation: return True # Create a mutable version of the GET parameters. query_dict = request.GET.copy() # Extract the signature we're going to verify. If no signature's present, the request is invalid. try: signature_to_verify = query_dict.pop('signature')[0] except KeyError: return False calculated_signature = get_proxy_signature(query_dict, secret) # Try to use compare_digest() to reduce vulnerability to timing attacks. # If it's not available, just fall back to regular string comparison. try: return hmac.compare_digest(calculated_signature.encode('utf-8'), signature_to_verify.encode('utf-8')) except AttributeError: return calculated_signature == signature_to_verify
def githook(req: HttpRequest): # check User-agent if not req.META.get('HTTP_USER_AGENT', '').startswith('GitHub-Hookshot'): return HttpResponse('UA error') # check event if not req.META.get('HTTP_X_GITHUB_EVENT', '') == 'push': return HttpResponse('Event error') try: # check sha1 signature name, req_sha1 = req.META.get('HTTP_X_HUB_SIGNATURE', '').split('=') if name != 'sha1': return HttpResponse('Signature format error') HOOK_SECRET_KEY = get_object_or_404(GithubHookSecret, pk=1) data = req.read() mac = hmac.new(HOOK_SECRET_KEY.secret.encode(), data, digestmod=hashlib.sha1) if not hmac.compare_digest(mac.hexdigest(), req_sha1): return HttpResponse('Signature check error') # get payload payload = json.loads(data.decode('utf-8'), encoding='utf-8') # check branch if payload['ref'].endswith('master'): script = os.path.join(settings.BASE_DIR, 'deploy.sh') proc = subprocess.Popen([script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return HttpResponse(proc.communicate()[0]) else: return HttpResponse('Not master branch, ignore') except Exception as e: return HttpResponse(str(e))
def verify_password(self, user, password): if user in self.passwd: _pwd = self.passwd[user] if not hmac.compare_digest(_pwd.encode(), password.encode()): raise FailedAuthentication('Wrong user/password combination') else: raise FailedAuthentication('Wrong user/password combination')
def async_validate_login(self, password: str) -> None: """Validate a username and password.""" hass_http = getattr(self.hass, 'http', None) # type: HomeAssistantHTTP if not hmac.compare_digest(hass_http.api_password.encode('utf-8'), password.encode('utf-8')): raise InvalidAuthError
def validate_signup_token(self, token): """ Validate a signup token Parameters ---------- token : str Returns ------- username : str or None This will be None if the validation fails """ if self.signing_key is None: return None pieces = token.split(":") signature = pieces.pop() username = pieces[0] issued = int(pieces[1]) if issued + self.token_expiration < time.time(): return None _, expected = self._hmac(username, issued) if hasattr(hmac, "compare_digest"): if isinstance(signature, six.text_type): signature = signature.encode("utf-8") if isinstance(expected, six.text_type): expected = expected.encode("utf-8") if not hmac.compare_digest(signature, expected): return None else: if signature != expected: return None return username
def Decrypt(fileProtected, cipher, digest, key, IV): fileToDeProtect = fileProtected.split('.')[0] + "." + fileProtected.split('.')[1] + ".dec" try: if hmac.compare_digest(CheckHash(fileProtected, key, IV), digest) == True: with open(fileProtected, 'rb') as inFile, open(fileToDeProtect, 'wa') as outFile: cryptData = inFile.read() clearData = cipher.decrypt(cryptData) cleanData = encoder.decode(clearData) outFile.write(cleanData) inFile.close() outFile.close() else: raise ValueError ('Data Not Secure') SendData('############################ Decrption Success') SendData('--> original file: ' + fileProtected) SendData('--> protected file: ' + fileToDeProtect) SendData('HashKEY: ' + base64.b64encode(DKey(key, IV))) SendData('HASH: Verified') SendData('----------------------------------------------') SendData('END') except: SendData('############################ Decrption Failed') SendData('Removing files!') if os.path.isfile(fileProtected ) == True: os.remove(fileProtected) SendData('File protected deleted') if os.path.isfile(fileToDeProtect) == True: os.remove(fileToDeProtect) SendData('File to de-protect deleted') else: SendData('--> no file here') SendData('----------------------------------------------') SendData('END')
def check_hash(cls, hash_, plaintext): if len(hash_) != cls._hash_length * 2: return False import hmac return hmac.compare_digest(cls.get_hash(plaintext), hash_)
def is_valid_signature(x_hub_signature, data, private_key): hash_algorithm, github_signature = x_hub_signature.split('=', 1) algorithm = hashlib.__dict__.get(hash_algorithm) encoded_key = bytes(private_key, 'latin-1') mac = hmac.new(encoded_key, msg=data, digestmod=algorithm) return hmac.compare_digest(mac.hexdigest(), github_signature)
def CheckString(check): HM3 = hmac.new(SignKey, check[:-32], hashlib.sha256) if hmac.compare_digest(HM3.digest(), check[-32:]) == True: pass else: sys.exit(1)
def is_assr_valid(self, key, assr): calc_assr = hmac.new(self.secret, key.encode("utf8"), hashlib.sha224).digest()[:12] return hmac.compare_digest(assr, calc_assr)
def deserialize( self, msg_list: t.Union[t.List[bytes], t.List[zmq.Message]], content: bool = True, copy: bool = True, ) -> t.Dict[str, t.Any]: """Unserialize a msg_list to a nested message dict. This is roughly the inverse of serialize. The serialize/deserialize methods work with full message lists, whereas pack/unpack work with the individual message parts in the message list. Parameters ---------- msg_list : list of bytes or Message objects The list of message parts of the form [HMAC,p_header,p_parent, p_metadata,p_content,buffer1,buffer2,...]. content : bool (True) Whether to unpack the content dict (True), or leave it packed (False). copy : bool (True) Whether msg_list contains bytes (True) or the non-copying Message objects in each place (False). Returns ------- msg : dict The nested message dict with top-level keys [header, parent_header, content, buffers]. The buffers are returned as memoryviews. """ minlen = 5 message = {} if not copy: # pyzmq didn't copy the first parts of the message, so we'll do it msg_list = t.cast(t.List[zmq.Message], msg_list) msg_list_beginning = [ bytes(msg.bytes) for msg in msg_list[:minlen] ] # type: ignore msg_list = t.cast(t.List[bytes], msg_list) msg_list = msg_list_beginning + msg_list[minlen:] msg_list = t.cast(t.List[bytes], msg_list) if self.auth is not None: signature = t.cast(bytes, msg_list[0]) if not signature: raise ValueError("Unsigned Message") if signature in self.digest_history: raise ValueError("Duplicate Signature: %r" % signature) if content: # Only store signature if we are unpacking content, don't store if just peeking. self._add_digest(signature) check = self.sign(msg_list[1:5]) if not compare_digest(signature, check): raise ValueError("Invalid Signature: %r" % signature) if not len(msg_list) >= minlen: raise TypeError( "malformed message, must have at least %i elements" % minlen) header = self.unpack(msg_list[1]) message["header"] = extract_dates(header) message["msg_id"] = header["msg_id"] message["msg_type"] = header["msg_type"] message["parent_header"] = extract_dates(self.unpack(msg_list[2])) message["metadata"] = self.unpack(msg_list[3]) if content: message["content"] = self.unpack(msg_list[4]) else: message["content"] = msg_list[4] buffers = [memoryview(b) for b in msg_list[5:]] if buffers and buffers[0].shape is None: # force copy to workaround pyzmq #646 msg_list = t.cast(t.List[zmq.Message], msg_list) buffers = [memoryview(bytes(b.bytes)) for b in msg_list[5:]] # type: ignore message["buffers"] = buffers if self.debug: pprint.pprint(message) # adapt to the current version return adapt(message)
def _verify_signature(payload, signature, secret): """Verify GitHub signature""" mac = hmac.new(secret, msg=payload, digestmod=hashlib.sha1) return hmac.compare_digest('sha1=' + mac.hexdigest(), signature)
def constant_time_compare(val1, val2): """Return True if the two strings are equal, False otherwise.""" return hmac.compare_digest(to_bytes(val1, 'utf8'), to_bytes(val2, 'utf8'))
def index(): """ Main WSGI application entry. """ path = normpath(abspath(dirname(__file__))) # Only POST is implemented if request.method != 'POST': abort(501) # Load config with open(join(path, 'config.json'), 'r') as cfg: config = loads(cfg.read()) hooks = config.get('hooks_path', join(path, 'hooks')) # Allow Github IPs only if config.get('github_ips_only', True): src_ip = ip_address( u'{}'.format(request.access_route[0]) # Fix stupid ipaddress issue ) whitelist = requests.get('https://api.github.com/meta').json()['hooks'] for valid_ip in whitelist: if src_ip in ip_network(valid_ip): break else: logging.error('IP {} not allowed'.format( src_ip )) abort(403) # Enforce secret secret = config.get('enforce_secret', '') if secret: # Only SHA1 is supported header_signature = request.headers.get('X-Hub-Signature') if header_signature is None: abort(403) sha_name, signature = header_signature.split('=') if sha_name != 'sha1': abort(501) # HMAC requires the key to be bytes, but data is string mac = hmac.new(str(secret), msg=request.data, digestmod=sha1) # Python prior to 2.7.7 does not have hmac.compare_digest if hexversion >= 0x020707F0: if not hmac.compare_digest(str(mac.hexdigest()), str(signature)): abort(403) else: # What compare_digest provides is protection against timing # attacks; we can live without this protection for a web-based # application if not str(mac.hexdigest()) == str(signature): abort(403) # Implement ping event = request.headers.get('X-GitHub-Event', 'ping') if event == 'ping': return dumps({'msg': 'pong'}) # Gather data try: payload = request.get_json() except Exception: logging.warning('Request parsing failed') abort(400) # Determining the branch is tricky, as it only appears for certain event # types an at different levels branch = None try: # Case 1: a ref_type indicates the type of ref. # This true for create and delete events. if 'ref_type' in payload: if payload['ref_type'] == 'branch': branch = payload['ref'] # Case 2: a pull_request object is involved. This is pull_request and # pull_request_review_comment events. elif 'pull_request' in payload: # This is the TARGET branch for the pull-request, not the source # branch branch = payload['pull_request']['base']['ref'] elif event in ['push']: # Push events provide a full Git ref in 'ref' and not a 'ref_type'. branch = payload['ref'].split('/', 2)[2] except KeyError: # If the payload structure isn't what we expect, we'll live without # the branch name pass # All current events have a repository, but some legacy events do not, # so let's be safe name = payload['repository']['name'] if 'repository' in payload else None meta = { 'name': name, 'branch': branch, 'event': event } logging.info('Metadata:\n{}'.format(dumps(meta))) # Skip push-delete if event == 'push' and payload['deleted']: logging.info('Skipping push-delete event for {}'.format(dumps(meta))) return dumps({'status': 'skipped'}) # Possible hooks scripts = [] if branch and name: scripts.append(join(hooks, '{event}-{name}-{branch}'.format(**meta))) if name: scripts.append(join(hooks, '{event}-{name}'.format(**meta))) scripts.append(join(hooks, '{event}'.format(**meta))) scripts.append(join(hooks, 'all')) # Check permissions scripts = [s for s in scripts if isfile(s) and access(s, X_OK)] if not scripts: return dumps({'status': 'nop'}) # Save payload to temporal file osfd, tmpfile = mkstemp() with fdopen(osfd, 'w') as pf: pf.write(dumps(payload)) # Run scripts ran = {} for s in scripts: proc = Popen( [s, tmpfile, event], stdout=PIPE, stderr=PIPE ) stdout, stderr = proc.communicate() ran[basename(s)] = { 'returncode': proc.returncode, 'stdout': stdout.decode('utf-8'), 'stderr': stderr.decode('utf-8'), } # Log errors if a hook failed if proc.returncode != 0: logging.error('{} : {} \n{}'.format( s, proc.returncode, stderr )) # Remove temporal file remove(tmpfile) info = config.get('return_scripts_info', False) if not info: return dumps({'status': 'done'}) output = dumps(ran, sort_keys=True, indent=4) logging.info(output) return output
def compare(a, b): """Safely compare two values""" return hmac.compare_digest(a, b)
def verify(self, entity_id): """Check if the signature matches the current namespace.""" entity_id, digest = self.parse(entity_id) if digest is None: return False return hmac.compare_digest(digest, self.signature(entity_id))
def verify_plaintext(request): """Verify a PLAINTEXT signature.""" sig = plaintext_signature(request.client_secret, request.token_secret) return hmac.compare_digest(sig, request.signature)
def validate_security_token(security_token, variable): if hmac.compare_digest(security_token, variable): return True return False
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: self._clear_old_nonces() if not self.hs.config.registration.registration_shared_secret: raise SynapseError(HTTPStatus.BAD_REQUEST, "Shared secret registration is not enabled") body = parse_json_object_from_request(request) if "nonce" not in body: raise SynapseError( HTTPStatus.BAD_REQUEST, "nonce must be specified", errcode=Codes.BAD_JSON, ) nonce = body["nonce"] if nonce not in self.nonces: raise SynapseError(HTTPStatus.BAD_REQUEST, "unrecognised nonce") # Delete the nonce, so it can't be reused, even if it's invalid del self.nonces[nonce] if "username" not in body: raise SynapseError( HTTPStatus.BAD_REQUEST, "username must be specified", errcode=Codes.BAD_JSON, ) else: if not isinstance(body["username"], str) or len(body["username"]) > 512: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username") username = body["username"].encode("utf-8") if b"\x00" in username: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username") if "password" not in body: raise SynapseError( HTTPStatus.BAD_REQUEST, "password must be specified", errcode=Codes.BAD_JSON, ) else: password = body["password"] if not isinstance(password, str) or len(password) > 512: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password") password_bytes = password.encode("utf-8") if b"\x00" in password_bytes: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password") password_hash = await self.auth_handler.hash(password) admin = body.get("admin", None) user_type = body.get("user_type", None) displayname = body.get("displayname", None) if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user type") if "mac" not in body: raise SynapseError(HTTPStatus.BAD_REQUEST, "mac must be specified", errcode=Codes.BAD_JSON) got_mac = body["mac"] want_mac_builder = hmac.new( key=self.hs.config.registration.registration_shared_secret.encode( ), digestmod=hashlib.sha1, ) want_mac_builder.update(nonce.encode("utf8")) want_mac_builder.update(b"\x00") want_mac_builder.update(username) want_mac_builder.update(b"\x00") want_mac_builder.update(password_bytes) want_mac_builder.update(b"\x00") want_mac_builder.update(b"admin" if admin else b"notadmin") if user_type: want_mac_builder.update(b"\x00") want_mac_builder.update(user_type.encode("utf8")) want_mac = want_mac_builder.hexdigest() if not hmac.compare_digest(want_mac.encode("ascii"), got_mac.encode("ascii")): raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect") # Reuse the parts of RegisterRestServlet to reduce code duplication from synapse.rest.client.register import RegisterRestServlet register = RegisterRestServlet(self.hs) user_id = await register.registration_handler.register_user( localpart=body["username"].lower(), password_hash=password_hash, admin=bool(admin), user_type=user_type, default_display_name=displayname, by_admin=True, ) result = await register._create_registration_details(user_id, body) return HTTPStatus.OK, result
def verify_hmac_sha1(request): """Verify a HMAC-SHA1 signature.""" base_string = generate_signature_base_string(request) sig = hmac_sha1_signature(base_string, request.client_secret, request.token_secret) return hmac.compare_digest(sig, request.signature)
def mac_verify(key, data, mac): computed_mac = mac_generate(key, data) result = hmac.compare_digest(mac.upper(), computed_mac.upper()) log.info("MAC of %r seems to be %r. Expected %r (%r)", data[:20], computed_mac[:20], mac[:20], result) return result
def _verify_hash(signature, s, alg): hash_value = create_half_hash(s, alg) if not hash_value: return True return hmac.compare_digest(hash_value, to_bytes(signature))
def autorize_user(self, message, sock): # Если имя пользователя уже занято то возвращаем 400 if message[USER][ACCOUNT_NAME] in self.names.keys(): response = RESPONSE_400 response[ERROR] = 'Имя пользователя уже занято.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() # Проверяем что пользователь зарегистрирован на сервере. elif not self.database.check_user(message[USER][ACCOUNT_NAME]): response = RESPONSE_400 response[ERROR] = 'Пользователь не зарегистрирован.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() else: # Иначе отвечаем 501 и проводим процедуру авторизации # Словарь - заготовка message_auth = RESPONSE_511 # Набор байтов в hex представлении random_str = binascii.hexlify(os.urandom(64)) # В словарь байты нельзя, декодируем (json.dumps -> TypeError) message_auth[DATA] = random_str.decode('ascii') # Создаём хэш пароля и связки с рандомной строкой, сохраняем серверную версию ключа hash = hmac.new(self.database.get_hash(message[USER][ACCOUNT_NAME]), random_str) digest = hash.digest() try: # Обмен с клиентом send_message(sock, message_auth) ans = get_message(sock) except OSError: sock.close() return client_digest = binascii.a2b_base64(ans[DATA]) # Если ответ клиента корректный, то сохраняем его в список пользователей. if RESPONSE in ans and ans[RESPONSE] == 511 and hmac.compare_digest(digest, client_digest): self.names[message[USER][ACCOUNT_NAME]] = sock client_ip, client_port = sock.getpeername() try: send_message(sock, RESPONSE_200) except OSError: self.remove_client(message[USER][ACCOUNT_NAME]) # добавляем пользователя в список активных и если у него изменился открытый ключ # сохраняем новый self.database.user_login(message[USER][ACCOUNT_NAME], client_ip, client_port, message[USER][PUBLIC_KEY]) else: response = RESPONSE_400 response[ERROR] = 'Неверный пароль.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close()
def valid_signature(headers, payload, secret): encoding = 'utf-8' algo, sig = headers.get('X-Hub-Signature').split('=') digest = hmac.new(secret.encode(encoding), payload.encode(encoding), getattr(hashlib, algo)).hexdigest() return hmac.compare_digest(sig.encode(encoding), digest.encode(encoding))
def assert_id(self, id, data): if id: id_computed = self.id_hash(data) if not compare_digest(id_computed, id): raise IntegrityError('Chunk %s: id verification failed' % bin_to_hex(id))
def compare(d1, d2): return hmac.compare_digest(_force_bytes(d1), _force_bytes(d2))
def verify_encrypted(encrypted, text): """ Return True/False if the encrypted content corresponds to the unencrypted text. """ return compare_digest(crypt(text, encrypted), encrypted)
def deserialize(self, msg_list, content=True, copy=True): """Unserialize a msg_list to a nested message dict. This is roughly the inverse of serialize. The serialize/deserialize methods work with full message lists, whereas pack/unpack work with the individual message parts in the message list. Parameters ---------- msg_list : list of bytes or Message objects The list of message parts of the form [HMAC,p_header,p_parent, p_metadata,p_content,buffer1,buffer2,...]. content : bool (True) Whether to unpack the content dict (True), or leave it packed (False). copy : bool (True) Whether msg_list contains bytes (True) or the non-copying Message objects in each place (False). Returns ------- msg : dict The nested message dict with top-level keys [header, parent_header, content, buffers]. The buffers are returned as memoryviews. """ minlen = 5 message = {} if not copy: # pyzmq didn't copy the first parts of the message, so we'll do it for i in range(minlen): msg_list[i] = msg_list[i].bytes if self.auth is not None: signature = msg_list[0] if not signature: raise ValueError("Unsigned Message") if signature in self.digest_history: raise ValueError("Duplicate Signature: %r" % signature) if content: # Only store signature if we are unpacking content, don't store if just peeking. self._add_digest(signature) check = self.sign(msg_list[1:5]) if not compare_digest(signature, check): raise ValueError("Invalid Signature: %r" % signature) if not len(msg_list) >= minlen: raise TypeError( "malformed message, must have at least %i elements" % minlen) header = self.unpack(msg_list[1]) message['header'] = extract_dates(header) message['msg_id'] = header['msg_id'] message['msg_type'] = header['msg_type'] message['parent_header'] = extract_dates(self.unpack(msg_list[2])) message['metadata'] = self.unpack(msg_list[3]) if content: message['content'] = self.unpack(msg_list[4]) else: message['content'] = msg_list[4] buffers = [memoryview(b) for b in msg_list[5:]] if buffers and buffers[0].shape is None: # force copy to workaround pyzmq #646 buffers = [memoryview(b.bytes) for b in msg_list[5:]] message['buffers'] = buffers if self.debug: pprint.pprint(message) cando_log("<<< deserialize", Session.session_log, message, Session.session_deserialize) # adapt to the current version return adapt(message)
def _authenticate_scram(credentials, sock_info, mechanism): """Authenticate using SCRAM.""" username = credentials.username if mechanism == 'SCRAM-SHA-256': digest = "sha256" digestmod = hashlib.sha256 data = saslprep(credentials.password).encode("utf-8") else: digest = "sha1" digestmod = hashlib.sha1 data = _password_digest(username, credentials.password).encode("utf-8") source = credentials.source cache = credentials.cache # Make local _hmac = hmac.HMAC user = username.encode("utf-8").replace(b"=", b"=3D").replace(b",", b"=2C") nonce = standard_b64encode(os.urandom(32)) first_bare = b"n=" + user + b",r=" + nonce cmd = SON([('saslStart', 1), ('mechanism', mechanism), ('payload', Binary(b"n,," + first_bare)), ('autoAuthorize', 1), ('options', { 'skipEmptyExchange': True })]) res = sock_info.command(source, cmd) server_first = res['payload'] parsed = _parse_scram_response(server_first) iterations = int(parsed[b'i']) if iterations < 4096: raise OperationFailure("Server returned an invalid iteration count.") salt = parsed[b's'] rnonce = parsed[b'r'] if not rnonce.startswith(nonce): raise OperationFailure("Server returned an invalid nonce.") without_proof = b"c=biws,r=" + rnonce if cache.data: client_key, server_key, csalt, citerations = cache.data else: client_key, server_key, csalt, citerations = None, None, None, None # Salt and / or iterations could change for a number of different # reasons. Either changing invalidates the cache. if not client_key or salt != csalt or iterations != citerations: salted_pass = _hi(digest, data, standard_b64decode(salt), iterations) client_key = _hmac(salted_pass, b"Client Key", digestmod).digest() server_key = _hmac(salted_pass, b"Server Key", digestmod).digest() cache.data = (client_key, server_key, salt, iterations) stored_key = digestmod(client_key).digest() auth_msg = b",".join((first_bare, server_first, without_proof)) client_sig = _hmac(stored_key, auth_msg, digestmod).digest() client_proof = b"p=" + standard_b64encode(_xor(client_key, client_sig)) client_final = b",".join((without_proof, client_proof)) server_sig = standard_b64encode( _hmac(server_key, auth_msg, digestmod).digest()) cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']), ('payload', Binary(client_final))]) res = sock_info.command(source, cmd) parsed = _parse_scram_response(res['payload']) if not compare_digest(parsed[b'v'], server_sig): raise OperationFailure("Server returned an invalid signature.") # A third empty challenge may be required if the server does not support # skipEmptyExchange: SERVER-44857. if not res['done']: cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']), ('payload', Binary(b''))]) res = sock_info.command(source, cmd) if not res['done']: raise OperationFailure('SASL conversation failed to complete.')
def webhook(): json = request.json # Get package github_url = "github.com/" + json["repository"]["full_name"] package = Package.query.filter( Package.repo.ilike("%{}%".format(github_url))).first() if package is None: return error( 400, "Could not find package, did you set the VCS repo in CDB correctly? Expected {}" .format(github_url)) # Get all tokens for package tokens_query = APIToken.query.filter( or_(APIToken.package == package, and_(APIToken.package == None, APIToken.owner == package.author))) possible_tokens = tokens_query.all() actual_token = None # # Check signature # header_signature = request.headers.get('X-Hub-Signature') if header_signature is None: return error(403, "Expected payload signature") sha_name, signature = header_signature.split('=') if sha_name != 'sha1': return error(403, "Expected SHA1 payload signature") for token in possible_tokens: mac = hmac.new(token.access_token.encode("utf-8"), msg=request.data, digestmod='sha1') if hmac.compare_digest(str(mac.hexdigest()), signature): actual_token = token break if actual_token is None: return error(403, "Invalid authentication, couldn't validate API token") if not package.checkPerm(actual_token.owner, Permission.APPROVE_RELEASE): return error(403, "You do not have the permission to approve releases") # # Check event # event = request.headers.get("X-GitHub-Event") if event == "push": ref = json["after"] title = json["head_commit"]["message"].partition("\n")[0] elif event == "create" and json["ref_type"] == "tag": ref = json["ref"] title = ref elif event == "ping": return jsonify({"success": True, "message": "Ping successful"}) else: return error( 400, "Unsupported event. Only 'push', `create:tag`, and 'ping' are supported." ) # # Perform release # return handleCreateRelease(actual_token, package, title, ref)
def slack_webhook(request): logger = logging.getLogger(__name__) logger.error("---------------- Slack Webhook -------------- ") logger.error( f"---------------- request.META: {request.META} -------------- ") # Get HMAC from request timestamp, hmac_digest = get_slack_hmac(request) slack_signature = request.META["HTTP_X_SLACK_SIGNATURE"] # The request timestamp is more than ten seconds from local time. # If so, we ignore it. occurred_in_last_10s = (int(datetime.datetime.now().timestamp()) - int(timestamp)) < 10 # If signatures don't match, or request occurred more than 5 minutes ago, discard request if not (hmac.compare_digest(slack_signature, hmac_digest) and occurred_in_last_10s): logger.error(f"slack_signature: {slack_signature}") logger.error(f"hmac_digest: {hmac_digest}") logger.error(f"occurred_in_last_10s: {occurred_in_last_10s}") capture_message( f"Old webhook or Invalid signature from Slack: discarding. request.body: {request.body}. slack_sig: {slack_signature}. computed_sig: {hmac_digest}. occurred_in_last_10s: {occurred_in_last_10s}." ) return HttpResponse( status=200) # Return a 200 so Slack doesn't keep retrying logger.error( "---------------- After invalid signature guard -------------- ") # Slack needs to verify the URL and does so by sending a "challenge" param when we set the URL in their # GUI here: https://api.slack.com/apps/AHB04HNE9/event-subscriptions # A payload with "challenge" is only sent when we set a new webhook URL if "challenge" in request.data: return JsonResponse({"challenge": request.data["challenge"]}) logger.error("---------------- After challenge guard -------------- ") # Handle app_uninstalled webhooks if request.data["event"]["type"] == "app_uninstalled": team_id = request.data["team_id"] try: # TODO: pretty sure we have a bug here. If you have multiple installs # for the same team it's going to generate a MutipleObjects exception. # We should probably just being doing filter(slack_team_id=team_id).delete(). # Don't have time to dig in on that right now. slack_settings = SlackSettings.objects.get(slack_team_id=team_id) except SlackSettings.DoesNotExist: return HttpResponse(status=200) logger.error("---------------- Uninstalled Slack app -------------- ") tracking.integration_disconnected(slack_settings.user, tracking.EVENT_SOURCE_SLACK) slack_settings.delete() return HttpResponse(status=200) logger.error( "---------------- After uninstall webhook guard -------------- ") channel_id = request.data["event"]["channel"] team_id = request.data["team_id"] # This section responds to a DM to SavioBot if request.data["event"]["channel_type"] == "im": # Ignore all requests with a bot_id in them - we don't want to respond to ourself! if "bot_id" in request.data["event"]: return HttpResponse(status=200) # This is a DM, so get the customer by team_id. try: slack_settings = SlackSettings.objects.get( slack_team_id=team_id, slack_feedback_channel_id=channel_id) except SlackSettings.DoesNotExist: return HttpResponse(status=406) headers = { "Content-Type": "application/json; charset=utf-8", "Authorization": "Bearer " + slack_settings.slack_bot_token, } msg_url = "https://slack.com/api/chat.postMessage" if request.data["event"]["text"] == "help": text = "I help you send customer feedback posted in Slack to Savio.\n\n" text = ( text + f"There are two ways to use Savio:\n\n1. Post customer feedback to the #{slack_settings.slack_feedback_channel_name} channel and I'll ask you if you want to send it to Savio. <https://www.youtube.com/watch?v=KOwnybk_clU|Watch a 30 second video.>" ) text = ( text + "\n2. Click the three dots to the right of any Slack message and choose 'Push to Savio'. <http://www.youtube.com/watch?v=DY7Ci5kUVG8|Watch a 30 second video.>" ) msg_json = { "channel": channel_id, "text": text, "link_names": True, "unfurl_media": False, } elif request.data["event"]["text"] == "power": text = f"Send feedback to Savio faster when you post Slack messages to #{slack_settings.slack_feedback_channel_name} by using this format:\n" text = ( text + "1. `[email protected]: Some feedback from your customer` OR \n2. `Customer Name: Some feedback from your customer`" ) text = ( text + f"\n\nWhen you use this format, we'll populate the Person dropdown with that person if they've been imported into Savio.\n\nWatch it in action: {settings.HOST}/static/images/help/slack-power-user.gif" ) msg_json = { "channel": channel_id, "text": text, "link_names": True, "unfurl_link": True, "unfurl_media": True, } else: msg_json = { "channel": channel_id, "text": "Sorry, I don't understand that. Please type 'help' or 'power' if you're a power user.", } requests.post(msg_url, data=json.dumps(msg_json), headers=headers) elif "user" in request.data["event"]: logger.error( "---------------- Not a DM, is Savio listing to channel? -------------- " ) # This is not a DM, so get the customer by channel_id to see if we care about messages posted to this channel. if request.data["event"]["type"] == "message": try: slack_settings = SlackSettings.objects.get( slack_team_id=team_id, slack_feedback_channel_id=channel_id) except SlackSettings.DoesNotExist: logger.error( f"---------------- Slack Settings does not exist for team_id {team_id} and slack_feedback_channel_id {channel_id}, returning 200 -------------- " ) return HttpResponse(status=200) logger.error( f"---------------- Message is user initiated. Slack_settings: {slack_settings} -------------- " ) # We have a user, which means this is a user-initiated message user_id = request.data["event"]["user"] logger.error( f"---------------- user_id == slack_settings.slack_bot_user_id: {user_id == slack_settings.slack_bot_user_id} -------------- " ) logger.error( f"---------------- request.data: {request.data} -------------- ") # Don't respond with ephemeral msg if user is Slack bot or if msg is # part of a thread or if there's a message subtype - we don't care about those. if (not (user_id == slack_settings.slack_bot_user_id) and ("thread_ts" not in request.data["event"]) and ("subtype" not in request.data["event"])): logger.error( "---------------- Inside Guard. Should return ephemeral message -------------- " ) message_ts = request.data["event"]["ts"] headers = { "Content-Type": "application/json; charset=utf-8", "Authorization": "Bearer " + slack_settings.slack_bot_token, } msg_url = "https://slack.com/api/chat.postEphemeral" msg_json = { "channel": channel_id, "user": user_id, "as_user": False, "text": "", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Is this customer feedback that you want to send to Savio?", }, }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "Yes" }, "value": message_ts, }, { "type": "button", "text": { "type": "plain_text", "text": "No" }, "value": "No", }, ], }, ], } requests.post(msg_url, data=json.dumps(msg_json), headers=headers) else: logger.error( "---------------- NOT inside Guard. NOT returning ephemeral message -------------- " ) return Response(status=status.HTTP_200_OK)
def file_upload(request): # If we're in read-only mode, let upload clients know if request.flags.enabled("read-only"): raise _exc_with_message( HTTPForbidden, "Read-only mode: Uploads are temporarily disabled") # Log an attempt to upload metrics = request.find_service(IMetricsService, context=None) metrics.increment("warehouse.upload.attempt") # Before we do anything, if there isn't an authenticated user with this # request, then we'll go ahead and bomb out. if request.authenticated_userid is None: raise _exc_with_message( HTTPForbidden, "Invalid or non-existent authentication information.") # Ensure that user has a verified, primary email address. This should both # reduce the ease of spam account creation and activity, as well as act as # a forcing function for https://github.com/pypa/warehouse/issues/3632. # TODO: Once https://github.com/pypa/warehouse/issues/3632 has been solved, # we might consider a different condition, possibly looking at # User.is_active instead. if not (request.user.primary_email and request.user.primary_email.verified): raise _exc_with_message( HTTPBadRequest, ("User {!r} does not have a verified primary email address. " "Please add a verified primary email before attempting to " "upload to PyPI. See {project_help} for more information." "for more information.").format( request.user.username, project_help=request.help_url(_anchor="verified-email"), ), ) from None # Do some cleanup of the various form fields for key in list(request.POST): value = request.POST.get(key) if isinstance(value, str): # distutils "helpfully" substitutes unknown, but "required" values # with the string "UNKNOWN". This is basically never what anyone # actually wants so we'll just go ahead and delete anything whose # value is UNKNOWN. if value.strip() == "UNKNOWN": del request.POST[key] # Escape NUL characters, which psycopg doesn't like if "\x00" in value: request.POST[key] = value.replace("\x00", "\\x00") # We require protocol_version 1, it's the only supported version however # passing a different version should raise an error. if request.POST.get("protocol_version", "1") != "1": raise _exc_with_message(HTTPBadRequest, "Unknown protocol version.") # Check if any fields were supplied as a tuple and have become a # FieldStorage. The 'content' and 'gpg_signature' fields _should_ be a # FieldStorage, however. # ref: https://github.com/pypa/warehouse/issues/2185 # ref: https://github.com/pypa/warehouse/issues/2491 for field in set(request.POST) - {"content", "gpg_signature"}: values = request.POST.getall(field) if any(isinstance(value, FieldStorage) for value in values): raise _exc_with_message(HTTPBadRequest, f"{field}: Should not be a tuple.") # Look up all of the valid classifiers all_classifiers = request.db.query(Classifier).all() # Validate and process the incoming metadata. form = MetadataForm(request.POST) # Add a validator for deprecated classifiers form.classifiers.validators.append(_no_deprecated_classifiers(request)) form.classifiers.choices = [(c.classifier, c.classifier) for c in all_classifiers] if not form.validate(): for field_name in _error_message_order: if field_name in form.errors: break else: field_name = sorted(form.errors.keys())[0] if field_name in form: field = form[field_name] if field.description and isinstance(field, wtforms.StringField): error_message = ( "{value!r} is an invalid value for {field}. ".format( value=field.data, field=field.description) + "Error: {} ".format(form.errors[field_name][0]) + "See " "https://packaging.python.org/specifications/core-metadata" ) else: error_message = "Invalid value for {field}. Error: {msgs[0]}".format( field=field_name, msgs=form.errors[field_name]) else: error_message = "Error: {}".format(form.errors[field_name][0]) raise _exc_with_message(HTTPBadRequest, error_message) # Ensure that we have file data in the request. if "content" not in request.POST: raise _exc_with_message(HTTPBadRequest, "Upload payload does not have a file.") # Look up the project first before doing anything else, this is so we can # automatically register it if we need to and can check permissions before # going any further. try: project = (request.db.query(Project).filter( Project.normalized_name == func.normalize_pep426_name( form.name.data)).one()) except NoResultFound: # Check for AdminFlag set by a PyPI Administrator disabling new project # registration, reasons for this include Spammers, security # vulnerabilities, or just wanting to be lazy and not worry ;) if request.flags.enabled("disallow-new-project-registration"): raise _exc_with_message( HTTPForbidden, ("New project registration temporarily disabled. " "See {projecthelp} for details").format( projecthelp=request.help_url( _anchor="admin-intervention")), ) from None # Before we create the project, we're going to check our blacklist to # see if this project is even allowed to be registered. If it is not, # then we're going to deny the request to create this project. if request.db.query(exists().where( BlacklistedProject.name == func.normalize_pep426_name( form.name.data))).scalar(): raise _exc_with_message( HTTPBadRequest, ("The name {name!r} isn't allowed. " "See {projecthelp} " "for more information.").format( name=form.name.data, projecthelp=request.help_url(_anchor="project-name"), ), ) from None # Also check for collisions with Python Standard Library modules. if packaging.utils.canonicalize_name( form.name.data) in STDLIB_PROHIBITTED: raise _exc_with_message( HTTPBadRequest, ("The name {name!r} isn't allowed (conflict with Python " "Standard Library module name). See " "{projecthelp} for more information.").format( name=form.name.data, projecthelp=request.help_url(_anchor="project-name"), ), ) from None # The project doesn't exist in our database, so first we'll check for # projects with a similar name squattees = (request.db.query(Project).filter( func.levenshtein(Project.normalized_name, func.normalize_pep426_name(form.name.data)) <= 2). all()) # Next we'll create the project project = Project(name=form.name.data) request.db.add(project) # Now that the project exists, add any squats which it is the squatter for for squattee in squattees: request.db.add(Squat(squatter=project, squattee=squattee)) # Then we'll add a role setting the current user as the "Owner" of the # project. request.db.add( Role(user=request.user, project=project, role_name="Owner")) # TODO: This should be handled by some sort of database trigger or a # SQLAlchemy hook or the like instead of doing it inline in this # view. request.db.add( JournalEntry( name=project.name, action="create", submitted_by=request.user, submitted_from=request.remote_addr, )) request.db.add( JournalEntry( name=project.name, action="add Owner {}".format(request.user.username), submitted_by=request.user, submitted_from=request.remote_addr, )) # Check that the user has permission to do things to this project, if this # is a new project this will act as a sanity check for the role we just # added above. if not request.has_permission("upload", project): raise _exc_with_message( HTTPForbidden, ("The user '{0}' isn't allowed to upload to project '{1}'. " "See {2} for more information.").format( request.user.username, project.name, request.help_url(_anchor="project-name"), ), ) # Update name if it differs but is still equivalent. We don't need to check if # they are equivalent when normalized because that's already been done when we # queried for the project. if project.name != form.name.data: project.name = form.name.data # Render our description so we can save from having to render this data every time # we load a project description page. rendered = None if form.description.data: description_content_type = form.description_content_type.data if not description_content_type: description_content_type = "text/x-rst" rendered = readme.render(form.description.data, description_content_type, use_fallback=False) # Uploading should prevent broken rendered descriptions. if rendered is None: if form.description_content_type.data: message = ( "The description failed to render " "for '{description_content_type}'.").format( description_content_type=description_content_type) else: message = ("The description failed to render " "in the default format of reStructuredText.") raise _exc_with_message( HTTPBadRequest, "{message} See {projecthelp} for more information.".format( message=message, projecthelp=request.help_url( _anchor="description-content-type"), ), ) from None try: canonical_version = packaging.utils.canonicalize_version( form.version.data) release = (request.db.query(Release).filter( (Release.project == project) & (Release.canonical_version == canonical_version)).one()) except MultipleResultsFound: # There are multiple releases of this project which have the same # canonical version that were uploaded before we checked for # canonical version equivalence, so return the exact match instead release = (request.db.query( Release).filter((Release.project == project) & (Release.version == form.version.data)).one()) except NoResultFound: release = Release( project=project, _classifiers=[ c for c in all_classifiers if c.classifier in form.classifiers.data ], dependencies=list( _construct_dependencies( form, { "requires": DependencyKind.requires, "provides": DependencyKind.provides, "obsoletes": DependencyKind.obsoletes, "requires_dist": DependencyKind.requires_dist, "provides_dist": DependencyKind.provides_dist, "obsoletes_dist": DependencyKind.obsoletes_dist, "requires_external": DependencyKind.requires_external, "project_urls": DependencyKind.project_url, }, )), canonical_version=canonical_version, description=Description( content_type=form.description_content_type.data, raw=form.description.data or "", html=rendered or "", rendered_by=readme.renderer_version(), ), **{ k: getattr(form, k).data for k in { # This is a list of all the fields in the form that we # should pull off and insert into our new release. "version", "summary", "license", "author", "author_email", "maintainer", "maintainer_email", "keywords", "platform", "home_page", "download_url", "requires_python", } }, uploader=request.user, uploaded_via=request.user_agent, ) request.db.add(release) # TODO: This should be handled by some sort of database trigger or # a SQLAlchemy hook or the like instead of doing it inline in # this view. request.db.add( JournalEntry( name=release.project.name, version=release.version, action="new release", submitted_by=request.user, submitted_from=request.remote_addr, )) # TODO: We need a better solution to this than to just do it inline inside # this method. Ideally the version field would just be sortable, but # at least this should be some sort of hook or trigger. releases = (request.db.query(Release).filter( Release.project == project).options( orm.load_only(Release._pypi_ordering)).all()) for i, r in enumerate( sorted(releases, key=lambda x: packaging.version.parse(x.version))): r._pypi_ordering = i # Pull the filename out of our POST data. filename = request.POST["content"].filename # Make sure that the filename does not contain any path separators. if "/" in filename or "\\" in filename: raise _exc_with_message( HTTPBadRequest, "Cannot upload a file with '/' or '\\' in the name.") # Make sure the filename ends with an allowed extension. if _dist_file_regexes[project.allow_legacy_files].search(filename) is None: raise _exc_with_message( HTTPBadRequest, "Invalid file extension: Use .egg, .tar.gz, .whl or .zip " "extension. (https://www.python.org/dev/peps/pep-0527)", ) # Make sure that our filename matches the project that it is being uploaded # to. prefix = pkg_resources.safe_name(project.name).lower() if not pkg_resources.safe_name(filename).lower().startswith(prefix): raise _exc_with_message( HTTPBadRequest, "Start filename for {!r} with {!r}.".format(project.name, prefix), ) # Check the content type of what is being uploaded if not request.POST["content"].type or request.POST[ "content"].type.startswith("image/"): raise _exc_with_message(HTTPBadRequest, "Invalid distribution file.") # Ensure that the package filetype is allowed. # TODO: Once PEP 527 is completely implemented we should be able to delete # this and just move it into the form itself. if not project.allow_legacy_files and form.filetype.data not in { "sdist", "bdist_wheel", "bdist_egg", }: raise _exc_with_message(HTTPBadRequest, "Unknown type of file.") # The project may or may not have a file size specified on the project, if # it does then it may or may not be smaller or larger than our global file # size limits. file_size_limit = max(filter(None, [MAX_FILESIZE, project.upload_limit])) with tempfile.TemporaryDirectory() as tmpdir: temporary_filename = os.path.join(tmpdir, filename) # Buffer the entire file onto disk, checking the hash of the file as we # go along. with open(temporary_filename, "wb") as fp: file_size = 0 file_hashes = { "md5": hashlib.md5(), "sha256": hashlib.sha256(), "blake2_256": hashlib.blake2b(digest_size=256 // 8), } for chunk in iter(lambda: request.POST["content"].file.read(8096), b""): file_size += len(chunk) if file_size > file_size_limit: raise _exc_with_message( HTTPBadRequest, "File too large. " + "Limit for project {name!r} is {limit} MB. ".format( name=project.name, limit=file_size_limit // (1024 * 1024)) + "See " + request.help_url(_anchor="file-size-limit"), ) fp.write(chunk) for hasher in file_hashes.values(): hasher.update(chunk) # Take our hash functions and compute the final hashes for them now. file_hashes = { k: h.hexdigest().lower() for k, h in file_hashes.items() } # Actually verify the digests that we've gotten. We're going to use # hmac.compare_digest even though we probably don't actually need to # because it's better safe than sorry. In the case of multiple digests # we expect them all to be given. if not all([ hmac.compare_digest( getattr(form, "{}_digest".format(digest_name)).data.lower(), digest_value, ) for digest_name, digest_value in file_hashes.items() if getattr(form, "{}_digest".format(digest_name)).data ]): raise _exc_with_message( HTTPBadRequest, "The digest supplied does not match a digest calculated " "from the uploaded file.", ) # Check to see if the file that was uploaded exists already or not. is_duplicate = _is_duplicate_file(request.db, filename, file_hashes) if is_duplicate: return Response() elif is_duplicate is not None: raise _exc_with_message( HTTPBadRequest, # Note: Changing this error message to something that doesn't # start with "File already exists" will break the # --skip-existing functionality in twine # ref: https://github.com/pypa/warehouse/issues/3482 # ref: https://github.com/pypa/twine/issues/332 "File already exists. See " + request.help_url(_anchor="file-name-reuse"), ) # Check to see if the file that was uploaded exists in our filename log if request.db.query( request.db.query(Filename).filter( Filename.filename == filename).exists()).scalar(): raise _exc_with_message( HTTPBadRequest, "This filename has already been used, use a " "different version. " "See " + request.help_url(_anchor="file-name-reuse"), ) # Check to see if uploading this file would create a duplicate sdist # for the current release. if (form.filetype.data == "sdist" and request.db.query( request.db.query(File).filter((File.release == release) & ( File.packagetype == "sdist")).exists()).scalar()): raise _exc_with_message( HTTPBadRequest, "Only one sdist may be uploaded per release.") # Check the file to make sure it is a valid distribution file. if not _is_valid_dist_file(temporary_filename, form.filetype.data): raise _exc_with_message(HTTPBadRequest, "Invalid distribution file.") # Check that if it's a binary wheel, it's on a supported platform if filename.endswith(".whl"): wheel_info = _wheel_file_re.match(filename) plats = wheel_info.group("plat").split(".") for plat in plats: if not _valid_platform_tag(plat): raise _exc_with_message( HTTPBadRequest, "Binary wheel '{filename}' has an unsupported " "platform tag '{plat}'.".format(filename=filename, plat=plat), ) # Also buffer the entire signature file to disk. if "gpg_signature" in request.POST: has_signature = True with open(os.path.join(tmpdir, filename + ".asc"), "wb") as fp: signature_size = 0 for chunk in iter( lambda: request.POST["gpg_signature"].file.read(8096), b""): signature_size += len(chunk) if signature_size > MAX_SIGSIZE: raise _exc_with_message(HTTPBadRequest, "Signature too large.") fp.write(chunk) # Check whether signature is ASCII armored with open(os.path.join(tmpdir, filename + ".asc"), "rb") as fp: if not fp.read().startswith(b"-----BEGIN PGP SIGNATURE-----"): raise _exc_with_message( HTTPBadRequest, "PGP signature isn't ASCII armored.") else: has_signature = False # TODO: This should be handled by some sort of database trigger or a # SQLAlchemy hook or the like instead of doing it inline in this # view. request.db.add(Filename(filename=filename)) # Store the information about the file in the database. file_ = File( release=release, filename=filename, python_version=form.pyversion.data, packagetype=form.filetype.data, comment_text=form.comment.data, size=file_size, has_signature=bool(has_signature), md5_digest=file_hashes["md5"], sha256_digest=file_hashes["sha256"], blake2_256_digest=file_hashes["blake2_256"], # Figure out what our filepath is going to be, we're going to use a # directory structure based on the hash of the file contents. This # will ensure that the contents of the file cannot change without # it also changing the path that the file is saved too. path="/".join([ file_hashes[PATH_HASHER][:2], file_hashes[PATH_HASHER][2:4], file_hashes[PATH_HASHER][4:], filename, ]), uploaded_via=request.user_agent, ) request.db.add(file_) # TODO: This should be handled by some sort of database trigger or a # SQLAlchemy hook or the like instead of doing it inline in this # view. request.db.add( JournalEntry( name=release.project.name, version=release.version, action="add {python_version} file {filename}".format( python_version=file_.python_version, filename=file_.filename), submitted_by=request.user, submitted_from=request.remote_addr, )) # TODO: We need a better answer about how to make this transactional so # this won't take affect until after a commit has happened, for # now we'll just ignore it and save it before the transaction is # committed. storage = request.find_service(IFileStorage) storage.store( file_.path, os.path.join(tmpdir, filename), meta={ "project": file_.release.project.normalized_name, "version": file_.release.version, "package-type": file_.packagetype, "python-version": file_.python_version, }, ) if has_signature: storage.store( file_.pgp_path, os.path.join(tmpdir, filename + ".asc"), meta={ "project": file_.release.project.normalized_name, "version": file_.release.version, "package-type": file_.packagetype, "python-version": file_.python_version, }, ) # Log a successful upload metrics.increment("warehouse.upload.ok", tags=[f"filetype:{form.filetype.data}"]) return Response()
def __eq__(self, other): return hmac.compare_digest(bytes(self), bytes(other))
def finalize_with_tag(self, tag): self.finalize() if not hmac.compare_digest(tag, self.__tag): raise bkx.InvalidTag # pragma: no cover
def constant_time_compare(val1, val2): """Return True if the two strings are equal, False otherwise.""" return hmac.compare_digest(force_bytes(val1), force_bytes(val2))
stats['Bytes_sent']) sock.sendto(fechaFinTransmision.encode(), addr) sock.sendto(paquetes_recibidos.encode(), addr) sock.sendto(bytes_recibidos.encode(), addr) print('Fecha fin transmision archivo: ', fechaFinTransmision) f.close() print('Comando recibido: ', repr(END_TRANSMISSION)) break digestG = digestGenerado.hexdigest().encode() print(digestG) #Se recibe el digest del servidor digestRecibido, addr = sock.recvfrom(buffer) print(digestRecibido) #Si el digest generado y el digest recibido no coinciden, se envia un mensaje de error. #Se finaliza la comunicacion con el servidor. if not compare_digest(digestG, digestRecibido): sock.sendto(ERR, addr) print('Comando enviado: ', repr(ERR)) print( 'La integridad del archivo no pudo ser verificada. Finalizando conexion.' ) sock.close() exit() sock.sendto(OK, addr) print('La integridad del archivo pudo ser verificada correctamente.') print('Comando enviado: ', repr(OK)) fin, _ = sock.recvfrom(buffer) if (repr(fin) != repr(FIN)):