-
Notifications
You must be signed in to change notification settings - Fork 83
Open
Description
🐛 Bug Report
The latest changes to SSLYZE cause a few import issues within pshtt.py.
To Reproduce
Run pshtt in conjunction with the latest version of sslyze available via PIP
Expected behavior
Pshtt succesfully implements https_check() to figure out the reason an endpoint wouldn't verify.
Any helpful log output
Import errors stemming from pshtt.py
To resolve these issues, I recommend either pinning the sslyze requirement to a previous version or implementing the following changes:
Sslyze-related imports within pshtt.py should be as follows (to maintain existing functionality):
import sslyze
from sslyze.server_connectivity import ServerConnectivityTester
from sslyze.errors import ConnectionToServerFailed
from sslyze.scanner import Scanner, ServerScanRequest, ScanCommandExtraArgumentsDict
from sslyze.plugins.scan_commands import ScanCommand
from sslyze.server_setting import ServerNetworkLocation, ServerNetworkLocationViaDirectConnection
It is worth noting that the latest changes to sslyze support asynchronous scanning. However, to simply maintain existing asynchronous scanning, "https_check()" could be amended as follows:
def https_check(endpoint):
"""
Uses sslyze to figure out the reason the endpoint wouldn't verify.
"""
# remove the https:// from prefix for sslyze
try:
hostname = endpoint.url[8:]
server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(hostname, 443)
server_tester = ServerConnectivityTester()
server_info = server_tester.perform(server_location)
endpoint.live = True
ip = server_location.ip_address
if endpoint.ip is None:
endpoint.ip = ip
else:
if endpoint.ip != ip:
logging.info("{}: Endpoint IP is already {}, but requests IP is {}.".format(endpoint.url, endpoint.ip, ip))
if server_info.tls_probing_result.client_auth_requirement.name == 'REQUIRED':
endpoint.https_client_auth_required = True
logging.warning("{}: Client Authentication REQUIRED".format(endpoint.url))
except ConnectionToServerFailed as err:
endpoint.live = False
endpoint.https_valid = False
logging.exception("{}: Error in sslyze server connectivity check when connecting to {}".format(endpoint.url, err.server_info.hostname))
return
except Exception as err:
endpoint.unknown_error = True
logging.exception("{}: Unknown exception in sslyze server connectivity check.".format(endpoint.url))
return
try:
cert_plugin_result = None
command = ScanCommand.CERTIFICATE_INFO
scanner = Scanner()
scan_request = ServerScanRequest(server_info=server_info, scan_commands=[command])
scanner.queue_scan(scan_request)
# Retrieve results from generator object
scan_result = [x for x in scanner.get_results()][0]
cert_plugin_result = scan_result.scan_commands_results['certificate_info']
except Exception as err:
try:
if "timed out" in str(err):
logging.exception("{}: Retrying sslyze scanner certificate plugin.".format(endpoint.url))
scanner.queue_scan(scan_request)
# Retrieve results from generator object
scan_result = [x for x in scanner.get_results()][0]
cert_plugin_result = scan_result.scan_commands_results['certificate_info']
else:
logging.exception("{}: Unknown exception in sslyze scanner certificate plugin.".format(endpoint.url))
endpoint.unknown_error = True
# We could make this False, but there was an error so
# we don't know
endpoint.https_valid = None
return
except Exception:
logging.exception("{}: Unknown exception in sslyze scanner certificate plugin.".format(endpoint.url))
endpoint.unknown_error = True
# We could make this False, but there was an error so we
# don't know
endpoint.https_valid = None
return
try:
public_trust = True
custom_trust = True
public_not_trusted_string = ""
validation_results = cert_plugin_result.certificate_deployments[0].path_validation_results
for result in validation_results:
if result.was_validation_successful:
# We're assuming that it is trusted to start with
pass
else:
if 'Custom' in result.trust_store.name:
custom_trust = False
else:
public_trust = False
if len(public_not_trusted_string) > 0:
public_not_trusted_string += ", "
public_not_trusted_string += result.trust_store.name
if public_trust:
logging.warning("{}: Publicly trusted by common trust stores.".format(endpoint.url))
else:
logging.warning("{}: Not publicly trusted - not trusted by {}.".format(endpoint.url, public_not_trusted_string))
custom_trust = None
endpoint.https_public_trusted = public_trust
endpoint.https_custom_trusted = custom_trust
except Exception as err:
# Ignore exception
logging.exception("{}: Unknown exception examining trust.".format(endpoint.url))
# Default endpoint assessments to False until proven True.
endpoint.https_expired_cert = False
endpoint.https_self_signed_cert = False
endpoint.https_bad_chain = False
endpoint.https_bad_hostname = False
cert_chain = cert_plugin_result.certificate_deployments[0].received_certificate_chain
# Check for missing SAN (Leaf certificate)
leaf_cert = cert_chain[0]
# Extract Subject Alternative Names
san_list = extract_dns_subject_alternative_names(leaf_cert)
# If an empty list was return, SAN(s) are missing. Bad hostname
if isinstance(san_list, list) and len(san_list) == 0:
endpoint.https_bad_hostname = True
# If leaf certificate subject does NOT match hostname, bad hostname
if not cert_plugin_result.certificate_deployments[0].leaf_certificate_subject_matches_hostname:
endpoint.https_bad_hostname = True
# Check for leaf certificate expiration/self-signature.
if leaf_cert.not_valid_after < datetime.datetime.now():
endpoint.https_expired_cert = True
# Check to see if the cert is self-signed
if leaf_cert.issuer is leaf_cert.subject:
endpoint.https_self_signed_cert = True
# Check certificate chain
for cert in cert_chain[1:]:
# Check for certificate expiration
if cert.not_valid_after < datetime.datetime.now():
endpoint.https_bad_chain = True
# Check to see if the cert is self-signed
if cert.issuer is (cert.subject or None):
endpoint.https_bad_chain = True
try:
endpoint.https_cert_chain_len = len(cert_plugin_result.certificate_deployments[0].received_certificate_chain)
if (
endpoint.https_self_signed_cert is False and (
len(cert_plugin_result.certificate_deployments[0].received_certificate_chain) < 2
)
):
# *** TODO check that it is not a bad hostname and that the root cert is trusted before suggesting that it is an intermediate cert issue.
endpoint.https_missing_intermediate_cert = True
if(cert_plugin_result.verified_certificate_chain is None):
logging.warning("{}: Untrusted certificate chain, probably due to missing intermediate certificate.".format(endpoint.url))
logging.info("{}: Only {} certificates in certificate chain received.".format(endpoint.url, cert_plugin_result.received_certificate_chain.__len__()))
else:
endpoint.https_missing_intermediate_cert = False
except Exception:
logging.exception("Error while determining length of certificate chain")
# If anything is wrong then https is not valid
if (
endpoint.https_expired_cert or
endpoint.https_self_signed_cert or
endpoint.https_bad_chain or
endpoint.https_bad_hostname
):
endpoint.https_valid = False
michaelblyons and conorsch
Metadata
Metadata
Assignees
Labels
No labels