def test_preloaded(self):
        result = is_hpkp_preloaded('apis.google.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])

        result = is_hpkp_preloaded('foo.apis.google.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])

        # uses include_subdomains_for_pinning
        result = is_hpkp_preloaded('dropboxstatic.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])
    def test_preloaded(self):
        result = is_hpkp_preloaded('apis.google.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])

        result = is_hpkp_preloaded('foo.apis.google.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])

        # uses include_subdomains_for_pinning
        result = is_hpkp_preloaded('dropboxstatic.com')

        self.assertTrue(result['pinned'])
        self.assertTrue(result['includeSubDomainsForPinning'])
    def test_preloaded(self):
        result = is_hpkp_preloaded("apis.google.com")

        self.assertTrue(result["pinned"])
        self.assertTrue(result["includeSubDomainsForPinning"])

        result = is_hpkp_preloaded("foo.apis.google.com")

        self.assertTrue(result["pinned"])
        self.assertTrue(result["includeSubDomainsForPinning"])

        # uses include_subdomains_for_pinning
        result = is_hpkp_preloaded("dropboxstatic.com")

        self.assertTrue(result["pinned"])
        self.assertTrue(result["includeSubDomainsForPinning"])

        # this domain is manually pinned
        result = is_hpkp_preloaded("aus4.mozilla.org")

        self.assertTrue(result["pinned"])
        self.assertTrue(result["includeSubDomainsForPinning"])
Example #4
0
def public_key_pinning(reqs: dict, expectation='hpkp-not-implemented') -> dict:
    """
    :param reqs: dictionary containing all the request and response objects
    :param expectation: test expectation; possible results:
      hpkp-not-implemented-no-https
      hpkp-not-implemented
      hpkp-implemented-max-age-less-than-fifteen-days
      hpkp-implemented-max-age-at-least-fifteen-days
      hpkp-preloaded
      hpkp-header-invalid
      hpkp-invalid-cert
    :return: dictionary with:
      data: the raw HPKP header
        includesubdomains: whether the includeSubDomains directive is set
        max-age: what the max
        num-pins: the number of pins
      expectation: test expectation
      pass: whether the site's configuration met its expectation
      result: short string describing the result of the test
    """
    FIFTEEN_DAYS = 1296000

    output = {
        'data': None,
        'expectation': expectation,
        'includeSubDomains': False,
        'max-age': None,
        'numPins': None,
        'pass': True,
        'preloaded': False,
        'result': 'hpkp-not-implemented',
    }
    response = reqs['responses']['https']

    # If there's no HTTPS, we can't have HPKP
    if response is None:
        output['result'] = 'hpkp-not-implemented-no-https'

    # Can't have HPKP without a valid certificate chain
    elif not response.verified:
        output['result'] = 'hpkp-invalid-cert'

    elif 'Public-Key-Pins' in response.headers:
        output['data'] = response.headers['Public-Key-Pins'][
            0:2048]  # code against malicious headers

        try:
            pkp = [i.lower().strip() for i in output['data'].split(';')]
            pins = []

            for parameter in pkp:
                if parameter.startswith('max-age='):
                    output['max-age'] = int(parameter[8:128])  # defense
                elif parameter.startswith(
                        'pin-sha256=') and parameter not in pins:
                    pins.append(parameter)
                elif parameter == 'includesubdomains':
                    output['includeSubDomains'] = True
            output['numPins'] = len(pins)

            # You must set a max-age with HPKP
            if output['max-age']:
                if output['max-age'] < FIFTEEN_DAYS:
                    output[
                        'result'] = 'hpkp-implemented-max-age-less-than-fifteen-days'
                else:
                    output[
                        'result'] = 'hpkp-implemented-max-age-at-least-fifteen-days'

            # You must have at least two pins with HPKP and set max-age
            if not output['max-age'] or len(pins) < 2:
                raise ValueError

        except:
            output['result'] = 'hpkp-header-invalid'
            output['pass'] = False

    # If they're in the preloaded list, this overrides most anything else
    if response is not None:
        preloaded = is_hpkp_preloaded(urlparse(response.url).netloc)
        if preloaded:
            output['result'] = 'hpkp-preloaded'
            output['includeSubDomains'] = preloaded[
                'includeSubDomainsForPinning']
            output['preloaded'] = True

    # No need to check pass/fail here, the only way to fail is to have an invalid header
    return output
    def test_not_preloaded(self):
        result = is_hpkp_preloaded("totallyfakehostname.insertsuperduperfakedomainhere.wtftld")

        self.assertFalse(result)
Example #6
0
def public_key_pinning(reqs: dict, expectation='hpkp-not-implemented') -> dict:
    """
    :param reqs: dictionary containing all the request and response objects
    :param expectation: test expectation; possible results:
      hpkp-not-implemented-no-https
      hpkp-not-implemented
      hpkp-implemented-max-age-less-than-fifteen-days
      hpkp-implemented-max-age-at-least-fifteen-days
      hpkp-preloaded
      hpkp-header-invalid
    :return: dictionary with:
      data: the raw HPKP header
        includesubdomains: whether the includeSubDomains directive is set
        max-age: what the max
        num-pins: the number of pins
      expectation: test expectation
      pass: whether the site's configuration met its expectation
      result: short string describing the result of the test
    """
    FIFTEEN_DAYS = 1296000

    output = {
        'data': None,
        'expectation': expectation,
        'includeSubDomains': False,
        'max-age': None,
        'numPins': None,
        'pass': True,
        'preloaded': False,
        'result': 'hpkp-not-implemented',
    }
    response = reqs['responses']['https']

    # If there's no HTTPS, we can't have HPKP
    if response is None:
        output['result'] = 'hpkp-not-implemented-no-https'

    elif 'Public-Key-Pins' in response.headers:
        output['data'] = response.headers['Public-Key-Pins'][0:2048]  # code against malicious headers

        try:
            pkp = [i.lower().strip() for i in output['data'].split(';')]
            pins = []

            for parameter in pkp:
                if parameter.startswith('max-age='):
                    output['max-age'] = int(parameter[8:128])  # defense
                elif parameter.startswith('pin-sha256=') and parameter not in pins:
                    pins.append(parameter)
                elif parameter == 'includesubdomains':
                    output['includeSubDomains'] = True
            output['numPins'] = len(pins)

            # You must set a max-age with HPKP
            if output['max-age']:
                if output['max-age'] < FIFTEEN_DAYS:
                    output['result'] = 'hpkp-implemented-max-age-less-than-fifteen-days'
                else:
                    output['result'] = 'hpkp-implemented-max-age-at-least-fifteen-days'

            # You must have at least two pins with HPKP and set max-age
            if not output['max-age'] or len(pins) < 2:
                raise ValueError

        except:
            output['result'] = 'hpkp-header-invalid'
            output['pass'] = False

    # If they're in the preloaded list, this overrides most anything else
    if response is not None:
        preloaded = is_hpkp_preloaded(urlparse(response.url).netloc)
        if preloaded:
            output['result'] = 'hpkp-preloaded'
            output['includeSubDomains'] = preloaded['includeSubDomainsForPinning']
            output['preloaded'] = True

    # No need to check pass/fail here, the only way to fail is to have an invalid header
    return output
Example #7
0
    def test_not_preloaded(self):
        result = is_hpkp_preloaded('totallyfakehostname.insertsuperduperfakedomainhere.wtftld')

        self.assertFalse(result)