Exemplo n.º 1
0
    def __init__(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: HttpHeadersScanCommand,
            raw_hsts_header: Optional[str],
            raw_hpkp_header: Optional[str],
            raw_expect_ct_header: Optional[str],
            hpkp_report_only: bool,
            cert_chain: List[Certificate],
    ) -> None:
        super().__init__(server_info, scan_command)
        self.hsts_header = ParsedHstsHeader(raw_hsts_header) if raw_hsts_header else None
        self.hpkp_header = ParsedHpkpHeader(raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None
        self.expect_ct_header = ParsedExpectCTHeader(raw_expect_ct_header) if raw_expect_ct_header else None
        self.verified_certificate_chain: List[Certificate] = []
        try:
            main_trust_store = TrustStoresRepository.get_default().get_main_store()
            self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain(cert_chain)
        except CouldNotBuildVerifiedChainError:
            pass

        # Is the pinning configuration valid?
        self.is_valid_pin_configured = None
        self.is_backup_pin_configured = None
        if self.verified_certificate_chain and self.hpkp_header:
            # Is one of the configured pins in the current server chain?
            self.is_valid_pin_configured = False
            server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain]
            for pin in self.hpkp_header.pin_sha256_list:
                if pin in server_pin_list:
                    self.is_valid_pin_configured = True
                    break

            # Is a backup pin configured?
            self.is_backup_pin_configured = set(self.hpkp_header.pin_sha256_list) != set(server_pin_list)
Exemplo n.º 2
0
    def as_text(self):
        txt_result = [self._format_title(self.scan_command.get_title())]

        if self.hsts_header:
            txt_result.append(self._format_subtitle('HTTP Strict Transport Security (HSTS)'))
            txt_result.append(self._format_field("Max Age:", str(self.hsts_header.max_age)))
            txt_result.append(self._format_field("Include Subdomains:", str(self.hsts_header.include_subdomains)))
            txt_result.append(self._format_field("Preload:", str(self.hsts_header.preload)))
        else:
            txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HSTS header", ""))

        computed_hpkp_pins_text = ['', self._format_subtitle('Computed HPKP Pins for Current Chain')]
        if self.verified_certificate_chain:
            for index, cert in enumerate(self.verified_certificate_chain, start=0):
                final_subject = CertificateUtils.get_name_as_short_text(cert.subject)
                if len(final_subject) > 40:
                    # Make the CN shorter when displaying it
                    final_subject = '{}...'.format(final_subject[:40])
                computed_hpkp_pins_text.append(
                    self.PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert))
                )
        else:
            computed_hpkp_pins_text.append(
                self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '')
            )

        txt_result.extend(['', self._format_subtitle('HTTP Public Key Pinning (HPKP)')])
        if self.hpkp_header:
            txt_result.append(self._format_field("Max Age:", str(self.hpkp_header.max_age)))
            txt_result.append(self._format_field("Include Subdomains:", str(self.hpkp_header.include_subdomains)))
            txt_result.append(self._format_field("Report URI:", self.hpkp_header.report_uri))
            txt_result.append(self._format_field("Report Only:", str(self.hpkp_header.report_only)))
            txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(self.hpkp_header.pin_sha256_list)))

            if self.verified_certificate_chain:
                pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \
                    if self.is_valid_pin_configured \
                    else 'FAILED - Could NOT find any of the configured pins in the certificate chain!'
                txt_result.append(self._format_field("Valid Pin:", pin_validation_txt))

                backup_txt = 'OK - Backup pin found in the configured pins' \
                    if self.is_backup_pin_configured \
                    else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!'
                txt_result.append(self._format_field("Backup Pin:", backup_txt))

        else:
            txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HPKP header", ""))

        # Dislpay computed HPKP pins
        txt_result.extend(computed_hpkp_pins_text)

        txt_result.extend(['', self._format_subtitle('HTTP Expect-CT')])
        if self.expect_ct_header:
          txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age)))
          txt_result.append(self._format_field('Report- URI:', self.expect_ct_header.report_uri))
          txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce)))
        else:
          txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an Expect-CT header", ""))

        return txt_result
Exemplo n.º 3
0
    def _certificate_chain_to_xml(certificate_chain: List[Certificate]) -> List[Element]:
        cert_xml_list = []
        for certificate in certificate_chain:
            cert_xml = Element('certificate', attrib={
                'sha1Fingerprint': binascii.hexlify(certificate.fingerprint(hashes.SHA1())).decode('ascii'),
                'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate)
            })

            # Add the PEM cert
            cert_as_pem_xml = Element('asPEM')
            cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM).decode('ascii')
            cert_xml.append(cert_as_pem_xml)

            # Add some of the fields of the cert
            elem_xml = Element('subject')
            elem_xml.text = CertificateUtils.get_name_as_text(certificate.subject)
            cert_xml.append(elem_xml)

            elem_xml = Element('issuer')
            elem_xml.text = CertificateUtils.get_name_as_text(certificate.issuer)
            cert_xml.append(elem_xml)

            elem_xml = Element('serialNumber')
            elem_xml.text = str(certificate.serial_number)
            cert_xml.append(elem_xml)

            elem_xml = Element('notBefore')
            elem_xml.text = certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S")
            cert_xml.append(elem_xml)

            elem_xml = Element('notAfter')
            elem_xml.text = certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S")
            cert_xml.append(elem_xml)

            elem_xml = Element('signatureAlgorithm')
            elem_xml.text = certificate.signature_hash_algorithm.name
            cert_xml.append(elem_xml)

            key_attrs = {'algorithm': CertificateUtils.get_public_key_type(certificate)}
            public_key = certificate.public_key()
            key_attrs['size'] = str(public_key.key_size)
            if isinstance(public_key, EllipticCurvePublicKey):
                key_attrs['curve'] = public_key.curve.name
            else:
                key_attrs['exponent'] = str(public_key.public_numbers().e)

            elem_xml = Element('publicKey', attrib=key_attrs)
            cert_xml.append(elem_xml)

            dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(certificate)
            if dns_alt_names:
                san_xml = Element('subjectAlternativeName')
                for dns_name in dns_alt_names:
                    dns_xml = Element('DNS')
                    dns_xml.text = dns_name
                    san_xml.append(dns_xml)
                cert_xml.append(san_xml)

            cert_xml_list.append(cert_xml)
        return cert_xml_list
Exemplo n.º 4
0
    def _object_to_json_dict(obj):
        """Convert an object to a dictionary suitable for the JSON output.
        """
        if isinstance(obj, Enum):
            # Properly serialize Enums (such as OpenSslVersionEnum)
            result = obj.name
        elif isinstance(obj, x509._Certificate):
            # Properly serialize certificates; only return the PEM string
            result = {
                'as_pem':
                obj.public_bytes(Encoding.PEM).decode('ascii'),
                'hpkp_pin':
                CertificateUtils.get_hpkp_pin(obj),
                'subject_name':
                CertificateUtils.get_name_as_short_text(obj.subject)
            }
        elif isinstance(obj, object):
            result = {}
            for key, value in obj.__dict__.items():
                # Remove private attributes
                if key.startswith('_'):
                    continue

                result[key] = value

        else:
            raise TypeError('Unknown type: {}'.format(repr(obj)))

        return result
Exemplo n.º 5
0
    def __init__(
            self,
            server_info,        # type: ServerConnectivityInfo
            scan_command,       # type: HttpHeadersScanCommand
            raw_hsts_header,    # type: Text
            raw_hpkp_header,    # type: Text
            hpkp_report_only,   # type: bool
            cert_chain          # type: List[cryptography.x509.Certificate]
    ):
        # type: (...) -> None
        super(HttpHeadersScanResult, self).__init__(server_info, scan_command)
        self.hsts_header = ParsedHstsHeader(raw_hsts_header) if raw_hsts_header else None
        self.hpkp_header = ParsedHpkpHeader(raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None

        self.verified_certificate_chain = []
        try:
            main_trust_store = TrustStoresRepository.get_default().get_main_store()
            self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain(cert_chain)
        except CouldNotBuildVerifiedChainError:
            pass

        # Is the pinning configuration valid?
        self.is_valid_pin_configured = None
        self.is_backup_pin_configured = None
        if self.verified_certificate_chain and self.hpkp_header:
            # Is one of the configured pins in the current server chain?
            self.is_valid_pin_configured = False
            server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain]
            for pin in self.hpkp_header.pin_sha256_list:
                if pin in server_pin_list:
                    self.is_valid_pin_configured = True
                    break

            # Is a backup pin configured?
            self.is_backup_pin_configured = set(self.hpkp_header.pin_sha256_list) != set(server_pin_list)
Exemplo n.º 6
0
    def _certificate_chain_to_xml(certificate_chain: List[Certificate]) -> List[Element]:
        cert_xml_list = []
        for certificate in certificate_chain:
            cert_xml = Element('certificate', attrib={
                'sha1Fingerprint': binascii.hexlify(certificate.fingerprint(hashes.SHA1())).decode('ascii'),
                'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate)
            })

            # Add the PEM cert
            cert_as_pem_xml = Element('asPEM')
            cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM).decode('ascii')
            cert_xml.append(cert_as_pem_xml)

            # Add some of the fields of the cert
            elem_xml = Element('subject')
            elem_xml.text = CertificateUtils.get_name_as_text(certificate.subject)
            cert_xml.append(elem_xml)

            elem_xml = Element('issuer')
            elem_xml.text = CertificateUtils.get_name_as_text(certificate.issuer)
            cert_xml.append(elem_xml)

            elem_xml = Element('serialNumber')
            elem_xml.text = str(certificate.serial_number)
            cert_xml.append(elem_xml)

            elem_xml = Element('notBefore')
            elem_xml.text = certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S")
            cert_xml.append(elem_xml)

            elem_xml = Element('notAfter')
            elem_xml.text = certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S")
            cert_xml.append(elem_xml)

            elem_xml = Element('signatureAlgorithm')
            elem_xml.text = certificate.signature_hash_algorithm.name
            cert_xml.append(elem_xml)

            key_attrs = {'algorithm': CertificateUtils.get_public_key_type(certificate)}
            public_key = certificate.public_key()
            key_attrs['size'] = str(public_key.key_size)
            if isinstance(public_key, EllipticCurvePublicKey):
                key_attrs['curve'] = public_key.curve.name
            else:
                key_attrs['exponent'] = str(public_key.public_numbers().e)

            elem_xml = Element('publicKey', attrib=key_attrs)
            cert_xml.append(elem_xml)

            dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(certificate)
            if dns_alt_names:
                san_xml = Element('subjectAlternativeName')
                for dns_name in dns_alt_names:
                    dns_xml = Element('DNS')
                    dns_xml.text = dns_name
                    san_xml.append(dns_xml)
                cert_xml.append(san_xml)

            cert_xml_list.append(cert_xml)
        return cert_xml_list
Exemplo n.º 7
0
    def as_text(self) -> List[str]:
        txt_result = [self._format_title(self.scan_command.get_title())]

        if self.hsts_header:
            txt_result.append(self._format_subtitle('HTTP Strict Transport Security (HSTS)'))
            txt_result.append(self._format_field("Max Age:", str(self.hsts_header.max_age)))
            txt_result.append(self._format_field("Include Subdomains:", str(self.hsts_header.include_subdomains)))
            txt_result.append(self._format_field("Preload:", str(self.hsts_header.preload)))
        else:
            txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HSTS header", ""))

        computed_hpkp_pins_text = ['', self._format_subtitle('Computed HPKP Pins for Current Chain')]
        if self.verified_certificate_chain:
            for index, cert in enumerate(self.verified_certificate_chain, start=0):
                final_subject = CertificateUtils.get_name_as_short_text(cert.subject)
                if len(final_subject) > 40:
                    # Make the CN shorter when displaying it
                    final_subject = '{}...'.format(final_subject[:40])
                computed_hpkp_pins_text.append(
                    self.PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert))
                )
        else:
            computed_hpkp_pins_text.append(
                self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '')
            )

        txt_result.extend(['', self._format_subtitle('HTTP Public Key Pinning (HPKP)')])
        if self.hpkp_header:
            txt_result.append(self._format_field("Max Age:", str(self.hpkp_header.max_age)))
            txt_result.append(self._format_field("Include Subdomains:", str(self.hpkp_header.include_subdomains)))
            txt_result.append(self._format_field("Report URI:", str(self.hpkp_header.report_uri)))
            txt_result.append(self._format_field("Report Only:", str(self.hpkp_header.report_only)))
            txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(self.hpkp_header.pin_sha256_list)))

            if self.verified_certificate_chain:
                pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \
                    if self.is_valid_pin_configured \
                    else 'FAILED - Could NOT find any of the configured pins in the certificate chain!'
                txt_result.append(self._format_field("Valid Pin:", pin_validation_txt))

                backup_txt = 'OK - Backup pin found in the configured pins' \
                    if self.is_backup_pin_configured \
                    else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!'
                txt_result.append(self._format_field("Backup Pin:", backup_txt))

        else:
            txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HPKP header", ""))

        # Dislpay computed HPKP pins
        txt_result.extend(computed_hpkp_pins_text)

        txt_result.extend(['', self._format_subtitle('HTTP Expect-CT')])
        if self.expect_ct_header:
            txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age)))
            txt_result.append(self._format_field('Report- URI:', str(self.expect_ct_header.report_uri)))
            txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce)))
        else:
            txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an Expect-CT header", ""))

        return txt_result
Exemplo n.º 8
0
def _object_to_json_dict(obj):
    """Convert an object to a dictionary suitable for the JSON output.
    """
    if isinstance(obj, Enum):
        # Properly serialize Enums (such as OpenSslVersionEnum)
        result = obj.name
    elif isinstance(obj, x509._Certificate):
        # Properly serialize certificates
        certificate = obj
        result = {
            # Add general info
            'as_pem': obj.public_bytes(Encoding.PEM).decode('ascii'),
            'hpkp_pin': CertificateUtils.get_hpkp_pin(obj),

            # Add some of the fields of the cert
            'subject': CertificateUtils.get_name_as_text(certificate.subject),
            'issuer': CertificateUtils.get_name_as_text(certificate.issuer),
            'serialNumber': str(certificate.serial_number),
            'notBefore':
            certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"),
            'notAfter':
            certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"),
            'signatureAlgorithm': certificate.signature_hash_algorithm.name,
            'publicKey': {
                'algorithm': CertificateUtils.get_public_key_type(certificate)
            },
        }

        dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(
            certificate)
        if dns_alt_names:
            result['subjectAlternativeName'] = {'DNS': dns_alt_names}

        # Add some info about the public key
        public_key = certificate.public_key()
        if isinstance(public_key, EllipticCurvePublicKey):
            result['publicKey']['size'] = str(public_key.curve.key_size)
            result['publicKey']['curve'] = public_key.curve.name
        else:
            result['publicKey']['size'] = str(public_key.key_size)
            result['publicKey']['exponent'] = str(
                public_key.public_numbers().e)

    elif isinstance(obj, object):
        if hasattr(obj, '__dict__'):
            result = {}
            for key, value in obj.__dict__.items():
                # Remove private attributes
                if key.startswith('_'):
                    continue

                result[key] = _object_to_json_dict(value)
        else:
            # Simple object like a string
            result = obj

    else:
        raise TypeError('Unknown type: {}'.format(repr(obj)))

    return result
Exemplo n.º 9
0
def _object_to_json_dict(obj: Any) -> Union[bool, int, float, str, Dict[str, Any]]:
    """Convert an object to a dictionary suitable for the JSON output.
    """
    if isinstance(obj, Enum):
        # Properly serialize Enums (such as OpenSslVersionEnum)
        result = obj.name

    elif isinstance(obj, x509._Certificate):
        # Properly serialize certificates
        certificate = obj
        result = {  # type: ignore
            # Add general info
            'as_pem': obj.public_bytes(Encoding.PEM).decode('ascii'),
            'hpkp_pin': CertificateUtils.get_hpkp_pin(obj),

            # Add some of the fields of the cert
            'subject': CertificateUtils.get_name_as_text(certificate.subject),
            'issuer': CertificateUtils.get_name_as_text(certificate.issuer),
            'serialNumber': str(certificate.serial_number),
            'notBefore': certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"),
            'notAfter': certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"),
            'signatureAlgorithm': certificate.signature_hash_algorithm.name,
            'publicKey': {
                'algorithm': CertificateUtils.get_public_key_type(certificate)
            },
        }

        dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(certificate)
        if dns_alt_names:
            result['subjectAlternativeName'] = {'DNS': dns_alt_names}  # type: ignore

        # Add some info about the public key
        public_key = certificate.public_key()
        if isinstance(public_key, EllipticCurvePublicKey):
            result['publicKey']['size'] = str(public_key.curve.key_size)  # type: ignore
            result['publicKey']['curve'] = public_key.curve.name  # type: ignore
        else:
            result['publicKey']['size'] = str(public_key.key_size)
            result['publicKey']['exponent'] = str(public_key.public_numbers().e)

    elif isinstance(obj, object):
        # Some objects (like str) don't have a __dict__
        if hasattr(obj, '__dict__'):
            result = {}
            for key, value in obj.__dict__.items():
                # Remove private attributes
                if key.startswith('_'):
                    continue

                result[key] = _object_to_json_dict(value)
        else:
            # Simple object like a bool
            result = obj

    else:
        raise TypeError('Unknown type: {}'.format(repr(obj)))

    return result
Exemplo n.º 10
0
    def __init__(
        self,
        server_info: ServerConnectivityInfo,
        scan_command: HttpHeadersScanCommand,
        raw_hsts_header: Optional[str],
        raw_hpkp_header: Optional[str],
        raw_expect_ct_header: Optional[str],
        hpkp_report_only: bool,
        cert_chain: List[Certificate],
    ) -> None:
        super().__init__(server_info, scan_command)
        self.hsts_header = ParsedHstsHeader(
            raw_hsts_header) if raw_hsts_header else None
        self.hpkp_header = ParsedHpkpHeader(
            raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None
        self.expect_ct_header = ParsedExpectCtHeader(
            raw_expect_ct_header) if raw_expect_ct_header else None
        self.verified_certificate_chain: List[Certificate] = []
        try:
            main_trust_store = TrustStoresRepository.get_default(
            ).get_main_store()
            self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain(
                cert_chain)
        except CouldNotBuildVerifiedChainError:
            pass

        # Is the pinning configuration valid?
        self.is_valid_pin_configured = None
        self.is_backup_pin_configured = None
        if self.verified_certificate_chain and self.hpkp_header:
            # Is one of the configured pins in the current server chain?
            self.is_valid_pin_configured = False
            server_pin_list = [
                CertificateUtils.get_hpkp_pin(cert)
                for cert in self.verified_certificate_chain
            ]
            for pin in self.hpkp_header.pin_sha256_list:
                if pin in server_pin_list:
                    self.is_valid_pin_configured = True
                    break

            # Is a backup pin configured?
            self.is_backup_pin_configured = set(
                self.hpkp_header.pin_sha256_list) != set(server_pin_list)
Exemplo n.º 11
0
    def __init__(
        self,
        server_info: ServerConnectivityInfo,
        scan_command: HttpHeadersScanCommand,
        strict_transport_security_header: Optional[
            StrictTransportSecurityHeader],
        public_key_pins_header: Optional[PublicKeyPinsHeader],
        public_key_pins_report_only_header: Optional[
            PublicKeyPinsReportOnlyHeader],
        expect_ct_header: Optional[ExpectCtHeader],
        verified_chain: Optional[List[Certificate]],
    ) -> None:
        super().__init__(server_info, scan_command)
        self.strict_transport_security_header = strict_transport_security_header
        self.public_key_pins_header = public_key_pins_header
        self.public_key_pins_report_only_header = public_key_pins_report_only_header
        self.expect_ct_header = expect_ct_header
        self.verified_certificate_chain = verified_chain

        # Is the pinning configuration valid?
        self.is_valid_pin_configured = None
        self.is_backup_pin_configured = None

        returned_hpkp_header = None
        if self.public_key_pins_header:
            returned_hpkp_header = self.public_key_pins_header
        elif self.public_key_pins_report_only_header:
            returned_hpkp_header = self.public_key_pins_report_only_header

        if self.verified_certificate_chain and returned_hpkp_header:
            # Is one of the configured pins in the current server chain?
            self.is_valid_pin_configured = False
            server_pin_list = [
                CertificateUtils.get_hpkp_pin(cert)
                for cert in self.verified_certificate_chain
            ]
            for pin in returned_hpkp_header.pin_sha256_list:
                if pin in server_pin_list:
                    self.is_valid_pin_configured = True
                    break

            # Is a backup pin configured?
            self.is_backup_pin_configured = set(
                returned_hpkp_header.pin_sha256_list) != set(server_pin_list)
Exemplo n.º 12
0
    def _certificate_chain_to_xml(certificate_chain):
        # type: (List[cryptography.x509.Certificate]) -> List[Element]
        cert_xml_list = []
        for certificate in certificate_chain:
            cert_xml = Element('certificate',
                               attrib={
                                   'sha1Fingerprint':
                                   binascii.hexlify(
                                       certificate.fingerprint(
                                           hashes.SHA1())).decode('ascii'),
                                   'hpkpSha256Pin':
                                   CertificateUtils.get_hpkp_pin(certificate)
                               })

            # Add the PEM cert
            cert_as_pem_xml = Element('asPEM')
            cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM)
            cert_xml.append(cert_as_pem_xml)
            cert_xml_list.append(cert_xml)
        return cert_xml_list
Exemplo n.º 13
0
    def __init__(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: HttpHeadersScanCommand,
            strict_transport_security_header: Optional[StrictTransportSecurityHeader],
            public_key_pins_header: Optional[PublicKeyPinsHeader],
            public_key_pins_report_only_header: Optional[PublicKeyPinsReportOnlyHeader],
            expect_ct_header: Optional[ExpectCtHeader],
            verified_chain: Optional[List[Certificate]],
    ) -> None:
        super().__init__(server_info, scan_command)
        self.strict_transport_security_header = strict_transport_security_header
        self.public_key_pins_header = public_key_pins_header
        self.public_key_pins_report_only_header = public_key_pins_report_only_header
        self.expect_ct_header = expect_ct_header
        self.verified_certificate_chain = verified_chain

        # Is the pinning configuration valid?
        self.is_valid_pin_configured = None
        self.is_backup_pin_configured = None

        returned_hpkp_header = None
        if self.public_key_pins_header:
            returned_hpkp_header = self.public_key_pins_header
        elif self.public_key_pins_report_only_header:
            returned_hpkp_header = self.public_key_pins_report_only_header

        if self.verified_certificate_chain and returned_hpkp_header:
            # Is one of the configured pins in the current server chain?
            self.is_valid_pin_configured = False
            server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain]
            for pin in returned_hpkp_header.pin_sha256_list:
                if pin in server_pin_list:
                    self.is_valid_pin_configured = True
                    break

            # Is a backup pin configured?
            self.is_backup_pin_configured = set(returned_hpkp_header.pin_sha256_list) != set(server_pin_list)
Exemplo n.º 14
0
    def default(self,
                obj: Any) -> Union[bool, int, float, str, Dict[str, Any]]:
        result: Union[bool, int, float, str, Dict[str, Any]]

        if isinstance(obj, Enum):
            result = obj.name

        elif isinstance(obj, ObjectIdentifier):
            result = obj.dotted_string

        elif isinstance(obj, x509._Certificate):
            certificate = obj
            result = {
                # Add general info
                "as_pem":
                obj.public_bytes(Encoding.PEM).decode("ascii"),
                "hpkp_pin":
                CertificateUtils.get_hpkp_pin(obj),
                # Add some of the fields of the cert
                "subject":
                CertificateUtils.get_name_as_text(certificate.subject),
                "issuer":
                CertificateUtils.get_name_as_text(certificate.issuer),
                "serialNumber":
                str(certificate.serial_number),
                "notBefore":
                certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"),
                "notAfter":
                certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"),
                "signatureAlgorithm":
                certificate.signature_hash_algorithm.name,
                "publicKey": {
                    "algorithm":
                    CertificateUtils.get_public_key_type(certificate)
                },
            }

            dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(
                certificate)
            if dns_alt_names:
                result["subjectAlternativeName"] = {
                    "DNS": dns_alt_names
                }  # type: ignore

            # Add some info about the public key
            public_key = certificate.public_key()
            if isinstance(public_key, EllipticCurvePublicKey):
                result["publicKey"]["size"] = str(
                    public_key.curve.key_size)  # type: ignore
                result["publicKey"][
                    "curve"] = public_key.curve.name  # type: ignore
            else:
                result["publicKey"]["size"] = str(public_key.key_size)
                result["publicKey"]["exponent"] = str(
                    public_key.public_numbers().e)

        elif isinstance(obj, Path):
            result = str(obj)

        elif isinstance(obj, object):
            # Some objects (like str) don't have a __dict__
            if hasattr(obj, "__dict__"):
                result = {}
                for key, value in obj.__dict__.items():
                    # Remove private attributes
                    if key.startswith("_"):
                        continue

                    result[key] = self.default(value)
            else:
                # Simple object like a bool
                result = obj  # type: ignore

        else:
            raise TypeError("Unknown type: {}".format(repr(obj)))

        return result
Exemplo n.º 15
0
    def as_xml(self):
        xml_output = Element(self.scan_command.get_cli_argument(),
                             title=self.COMMAND_TITLE)

        # Certificate chain
        cert_xml_list = []
        for index, certificate in enumerate(self.certificate_chain, start=0):
            cert_xml = Element('certificate',
                               attrib={
                                   'sha1Fingerprint':
                                   binascii.hexlify(
                                       certificate.fingerprint(
                                           hashes.SHA1())).decode('ascii'),
                                   'position':
                                   'leaf' if index == 0 else 'intermediate',
                                   'suppliedServerNameIndication':
                                   self.server_info.tls_server_name_indication,
                                   'hpkpSha256Pin':
                                   CertificateUtils.get_hpkp_pin(certificate)
                               })

            # Add the PEM cert
            cert_as_pem_xml = Element('asPEM')
            cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM)
            cert_xml.append(cert_as_pem_xml)

        cert_chain_attrs = {
            'isChainOrderValid': str(self.is_certificate_chain_order_valid)
        }
        if self.verified_certificate_chain:
            cert_chain_attrs['containsAnchorCertificate'] = str(False) if not self.has_anchor_in_certificate_chain \
                else str(True)
        cert_chain_xml = Element('receivedCertificateChain',
                                 attrib=cert_chain_attrs)

        for cert_xml in cert_xml_list:
            cert_chain_xml.append(cert_xml)
        xml_output.append(cert_chain_xml)

        # Trust
        trust_validation_xml = Element('certificateValidation')

        # Hostname validation
        host_validation_xml = Element(
            'hostnameValidation',
            serverHostname=self.server_info.tls_server_name_indication,
            certificateMatchesServerHostname=str(
                self.certificate_matches_hostname))
        trust_validation_xml.append(host_validation_xml)

        # Path validation that was successful
        for path_result in self.path_validation_result_list:
            path_attrib_xml = {
                'usingTrustStore': path_result.trust_store.name,
                'trustStoreVersion': path_result.trust_store.version,
                'validationResult': path_result.verify_string
            }

            # Things we only do with the Mozilla store
            verified_cert_chain_xml = None
            if 'Mozilla' in path_result.trust_store.name:
                # EV certs
                if self.is_leaf_certificate_ev:
                    path_attrib_xml['isExtendedValidationCertificate'] = str(
                        self.is_leaf_certificate_ev)

                # Verified chain
                if self.verified_certificate_chain:
                    verified_cert_chain_xml = Element(
                        'verifiedCertificateChain', {
                            'hasSha1SignedCertificate':
                            str(self.has_sha1_in_certificate_chain)
                        })
                    for certificate in self.certificate_chain:
                        cert_xml = Element(
                            'certificate',
                            attrib={
                                'sha1Fingerprint':
                                binascii.hexlify(
                                    certificate.fingerprint(
                                        hashes.SHA1())).decode('ascii'),
                                'suppliedServerNameIndication':
                                self.server_info.tls_server_name_indication,
                                'hpkpSha256Pin':
                                CertificateUtils.get_hpkp_pin(certificate)
                            })

                        # Add the PEM cert
                        cert_as_pem_xml = Element('asPEM')
                        cert_as_pem_xml.text = certificate.public_bytes(
                            Encoding.PEM)
                        cert_xml.append(cert_as_pem_xml)

                        verified_cert_chain_xml.append(cert_xml)

            path_valid_xml = Element('pathValidation', attrib=path_attrib_xml)
            if verified_cert_chain_xml is not None:
                path_valid_xml.append(verified_cert_chain_xml)

            trust_validation_xml.append(path_valid_xml)

        # Path validation that ran into errors
        for path_error in self.path_validation_error_list:
            error_txt = 'ERROR: {}'.format(path_error.error_message)
            path_attrib_xml = {
                'usingTrustStore': path_result.trust_store.name,
                'trustStoreVersion': path_result.trust_store.version,
                'error': error_txt
            }

            trust_validation_xml.append(
                Element('pathValidation', attrib=path_attrib_xml))

        xml_output.append(trust_validation_xml)

        # OCSP Stapling
        ocsp_xml = Element(
            'ocspStapling',
            attrib={
                'isSupported':
                'False' if self.ocsp_response is None else 'True'
            })

        if self.ocsp_response:
            ocsp_resp_xmp = Element('ocspResponse',
                                    attrib={
                                        'isTrustedByMozillaCAStore':
                                        str(self.is_ocsp_response_trusted)
                                    })

            responder_xml = Element('responderID')
            responder_xml.text = self.ocsp_response['responderID']
            ocsp_resp_xmp.append(responder_xml)

            produced_xml = Element('producedAt')
            produced_xml.text = self.ocsp_response['producedAt']
            ocsp_resp_xmp.append(produced_xml)

            response_status_xml = Element('responseStatus')
            response_status_xml.text = self.ocsp_response['responseStatus']
            ocsp_resp_xmp.append(response_status_xml)

            ocsp_xml.append(ocsp_resp_xmp)
        xml_output.append(ocsp_xml)

        # All done
        return xml_output
Exemplo n.º 16
0
    def as_text(self) -> List[str]:
        txt_result = [self._format_title(self.scan_command.get_title()), ""]

        txt_result.append(
            self._format_subtitle(
                "Computed HPKP Pins for Server Certificate Chain"))
        if self.verified_certificate_chain:
            for index, cert in enumerate(self.verified_certificate_chain,
                                         start=0):
                final_subject = CertificateUtils.get_name_as_short_text(
                    cert.subject)
                if len(final_subject) > 40:
                    # Make the CN shorter when displaying it
                    final_subject = "{}...".format(final_subject[:40])
                txt_result.append(
                    self._PIN_TXT_FORMAT(
                        ("{} - {}".format(index, final_subject)),
                        CertificateUtils.get_hpkp_pin(cert)))
                txt_result.append("")
        else:
            txt_result.append(
                self._format_field(
                    "ERROR - Could not build verified chain (certificate untrusted?)",
                    ""))
            txt_result.append("")

        txt_result.append(
            self._format_subtitle("Strict-Transport-Security Header"))
        if self.strict_transport_security_header:
            txt_result.append(
                self._format_field(
                    "Max Age:",
                    str(self.strict_transport_security_header.max_age)))
            txt_result.append(
                self._format_field(
                    "Include Subdomains:",
                    str(self.strict_transport_security_header.
                        include_subdomains)))
            txt_result.append(
                self._format_field(
                    "Preload:",
                    str(self.strict_transport_security_header.preload)))
        else:
            txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT,
                                                 ""))

        for header, subtitle in [
            (self.public_key_pins_header, "Public-Key-Pins Header"),
            (self.public_key_pins_report_only_header,
             "Public-Key-Pins-Report-Only Header"),
        ]:
            txt_result.extend(["", self._format_subtitle(subtitle)])
            if header:
                txt_result.append(
                    self._format_field("Max Age:", str(header.max_age)))
                txt_result.append(
                    self._format_field("Include Subdomains:",
                                       str(header.include_subdomains)))
                txt_result.append(
                    self._format_field("Report URI:", str(header.report_uri)))
                txt_result.append(
                    self._format_field("SHA-256 Pin List:",
                                       ", ".join(header.pin_sha256_list)))

                if self.verified_certificate_chain:
                    pin_validation_txt = (
                        "OK - One of the configured pins was found in the certificate chain"
                        if self.is_valid_pin_configured else
                        "FAILED - Could NOT find any of the configured pins in the certificate chain!"
                    )
                    txt_result.append(
                        self._format_field("Valid Pin:", pin_validation_txt))

                    backup_txt = (
                        "OK - Backup pin found in the configured pins"
                        if self.is_backup_pin_configured else
                        "FAILED - No backup pin found: all the configured pins are in the certificate chain!"
                    )
                    txt_result.append(
                        self._format_field("Backup Pin:", backup_txt))

            else:
                txt_result.append(
                    self._format_field(self._HEADER_NOT_SENT_TXT, ""))

        txt_result.extend(["", self._format_subtitle("Expect-CT Header")])
        if self.expect_ct_header:
            txt_result.append(
                self._format_field("Max Age:",
                                   str(self.expect_ct_header.max_age)))
            txt_result.append(
                self._format_field("Report- URI:",
                                   str(self.expect_ct_header.report_uri)))
            txt_result.append(
                self._format_field("Enforce:",
                                   str(self.expect_ct_header.enforce)))
        else:
            txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT,
                                                 ""))

        return txt_result
Exemplo n.º 17
0
    def as_text(self) -> List[str]:
        txt_result = [self._format_title(self.scan_command.get_title()), '']

        txt_result.append(self._format_subtitle('Computed HPKP Pins for Server Certificate Chain'))
        if self.verified_certificate_chain:
            for index, cert in enumerate(self.verified_certificate_chain, start=0):
                final_subject = CertificateUtils.get_name_as_short_text(cert.subject)
                if len(final_subject) > 40:
                    # Make the CN shorter when displaying it
                    final_subject = '{}...'.format(final_subject[:40])
                txt_result.append(
                    self._PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert))
                )
                txt_result.append('')
        else:
            txt_result.append(
                self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '')
            )
            txt_result.append('')

        txt_result.append(self._format_subtitle('Strict-Transport-Security Header'))
        if self.strict_transport_security_header:
            txt_result.append(self._format_field("Max Age:", str(self.strict_transport_security_header.max_age)))
            txt_result.append(self._format_field(
                "Include Subdomains:", str(self.strict_transport_security_header.include_subdomains))
            )
            txt_result.append(self._format_field("Preload:", str(self.strict_transport_security_header.preload)))
        else:
            txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, ""))

        for header, subtitle in [
            (self.public_key_pins_header, 'Public-Key-Pins Header'),
            (self.public_key_pins_report_only_header, 'Public-Key-Pins-Report-Only Header')
        ]:
            txt_result.extend(['', self._format_subtitle(subtitle)])
            if header:
                txt_result.append(self._format_field("Max Age:", str(header.max_age)))
                txt_result.append(self._format_field("Include Subdomains:", str(header.include_subdomains)))
                txt_result.append(self._format_field("Report URI:", str(header.report_uri)))
                txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(header.pin_sha256_list)))

                if self.verified_certificate_chain:
                    pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \
                        if self.is_valid_pin_configured \
                        else 'FAILED - Could NOT find any of the configured pins in the certificate chain!'
                    txt_result.append(self._format_field("Valid Pin:", pin_validation_txt))

                    backup_txt = 'OK - Backup pin found in the configured pins' \
                        if self.is_backup_pin_configured \
                        else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!'
                    txt_result.append(self._format_field("Backup Pin:", backup_txt))

            else:
                txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, ""))

        txt_result.extend(['', self._format_subtitle('Expect-CT Header')])
        if self.expect_ct_header:
            txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age)))
            txt_result.append(self._format_field('Report- URI:', str(self.expect_ct_header.report_uri)))
            txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce)))
        else:
            txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, ""))

        return txt_result