Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

feat: remove hpkp tests #521

Merged
merged 2 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ $ poetry install
path='/foo/bar', # don't scan /, instead scan /foo/bar
cookies={'foo': 'bar'}, # set the "foo" cookie to "bar"
headers={'X-Foo': 'bar'}, # send an X-Foo: bar HTTP header
verify=False) # treat self-signed certs as valid for tests like HSTS/HPKP
verify=False) # treat self-signed certs as valid for tests like HSTS
```

### The same, but with the local CLI
Expand Down
15 changes: 0 additions & 15 deletions httpobs/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,21 +394,6 @@ Example:
"score_description": "Content is not visible via cross-origin resource sharing (CORS) files or headers",
"score_modifier": 0
},
"public-key-pinning": {
"expectation": "hpkp-not-implemented",
"name": "public-key-pinning",
"output": {
"data": null,
"includeSubDomains": false,
"max-age": null,
"numPins": null,
"preloaded": false
},
"pass": true,
"result": "hpkp-not-implemented",
"score_description": "HTTP Public Key Pinning (HPKP) header not implemented",
"score_modifier": 0
},
"redirection": {
"expectation": "redirection-to-https",
"name": "redirection",
Expand Down
11 changes: 0 additions & 11 deletions httpobs/docs/scoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,6 @@ csp-not-implemented | Content Security Policy (CSP) header not implemented | -25
csp-header-invalid | Content Security Policy (CSP) header cannot be parsed successfully | -25
<br>

[HTTP Public Key Pinning](https://infosec.mozilla.org/guidelines/web_security#http-public-key-pinning) | Description | Modifier
--- | --- | :---:
hpkp-preloaded | Preloaded via the HTTP Public Key Pinning (HPKP) preloading process | 0
hpkp-implemented-<br>max-age-at-least-fifteen-days | HTTP Public Key Pinning (HPKP) header set to a minimum of 15 days (1296000) | 0
hpkp-implemented-<br>max-age-less-than-fifteen-days | HTTP Public Key Pinning (HPKP) header set to less than 15 days (1296000) | 0
hpkp-not-implemented | HTTP Public Key Pinning (HPKP) header not implemented | 0
hpkp-invalid-cert | HTTP Public Key Pinning (HPKP) header cannot be set, as site contains an invalid certificate chain | 0
hpkp-not-implemented-no-https | HTTP Public Key Pinning (HPKP) header can't be implemented without https | 0
hpkp-header-invalid | HTTP Public Key Pinning (HPKP) header cannot be recognized | -5
<br>

[HTTP Strict Transport Security](https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security) | Description | Modifier
--- | --- | :---:
hsts-preloaded | Preloaded via the HTTP Strict Transport Security (HSTS) preloading process | 5
Expand Down
2 changes: 0 additions & 2 deletions httpobs/scanner/analyzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from .headers import (
content_security_policy,
cookies,
public_key_pinning,
referrer_policy,
strict_transport_security,
x_content_type_options,
Expand All @@ -18,7 +17,6 @@
cookies,
contribute,
cross_origin_resource_sharing,
public_key_pinning,
redirection,
referrer_policy,
strict_transport_security,
Expand Down
88 changes: 1 addition & 87 deletions httpobs/scanner/analyzer/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from urllib.parse import urlparse, urlunparse

from httpobs.scanner.analyzer.decorators import scored_test
from httpobs.scanner.analyzer.utils import is_hpkp_preloaded, is_hsts_preloaded, only_if_worse
from httpobs.scanner.analyzer.utils import is_hsts_preloaded, only_if_worse
from httpobs.scanner.retriever import get_duplicate_header_values

# Ignore the CloudFlare __cfduid tracking cookies. They *are* actually bad, but it is out of a site's
Expand Down Expand Up @@ -488,92 +488,6 @@ def cookies(reqs: dict, expectation='cookies-secure-with-httponly-sessions') ->
return output


@scored_test
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


@scored_test
def referrer_policy(reqs: dict, expectation='referrer-policy-private') -> dict:
"""
Expand Down
19 changes: 0 additions & 19 deletions httpobs/scanner/analyzer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,6 @@
hsts = json.load(f)


def is_hpkp_preloaded(hostname):
# Just see if the hostname is in the HSTS list and pinned
if hsts.get(hostname, {}).get('pinned'):
return hsts[hostname]

# Either the hostname is in the list *or* one of its subdomains is
host = hostname.split('.')
levels = len(host)

# If hostname is foo.bar.baz.mozilla.org, check bar.baz.mozilla.org, baz.mozilla.org, mozilla.org, and .org
for i in range(1, levels):
domain = '.'.join(host[i:levels])

if hsts.get(domain, {}).get('pinned') is True and hsts.get(domain, {}).get('includeSubDomainsForPinning'):
return hsts[domain]

return False


def is_hsts_preloaded(hostname):
# Just see if the hostname is the HSTS list with the right mode -- no need to check includeSubDomains
if hsts.get(hostname, {}).get('mode') == 'force-https':
Expand Down
31 changes: 0 additions & 31 deletions httpobs/scanner/grader/grade.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,37 +171,6 @@
'description': 'Content is visible via cross-origin resource sharing (CORS) file or headers',
'modifier': -50,
},
# Public Key Pinning
'hpkp-preloaded': {
'description': 'Preloaded via the HTTP Public Key Pinning (HPKP) preloading process',
'modifier': 0,
},
'hpkp-implemented-max-age-at-least-fifteen-days': {
'description': 'HTTP Public Key Pinning (HPKP) header set to a minimum of 15 days (1296000)',
'modifier': 0,
},
'hpkp-implemented-max-age-less-than-fifteen-days': {
'description': 'HTTP Public Key Pinning (HPKP) header set to less than 15 days (1296000)',
'modifier': 0,
},
'hpkp-not-implemented': {
'description': 'HTTP Public Key Pinning (HPKP) header not implemented',
'modifier': 0,
},
'hpkp-not-implemented-no-https': {
'description': 'HTTP Public Key Pinning (HPKP) header can\'t be implemented without HTTPS',
'modifier': 0,
},
'hpkp-invalid-cert': {
'description': (
'HTTP Public Key Pinning (HPKP) header cannot be set, ' 'as site contains an invalid certificate chain'
),
'modifier': 0,
},
'hpkp-header-invalid': {
'description': 'HTTP Public Key Pinning (HPKP) header cannot be recognized',
'modifier': -5,
},
# Redirection
'redirection-all-redirects-preloaded': {
'description': 'All hosts redirected to are in the HTTP Strict Transport Security (HSTS) preload list',
Expand Down
2 changes: 1 addition & 1 deletion httpobs/scanner/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def scan(hostname: str, **kwargs):
path (str): path to scan, instead of "/"
verify (bool): whether to enable or disable certificate verification,
enabled by default. This can allow tested sites to pass the HSTS
and HPKP tests, even with self-signed certificates.
tests, even with self-signed certificates.

cookies (dict): Cookies sent to the system being scanned. Matches the
requests cookie dict.
Expand Down
4 changes: 1 addition & 3 deletions httpobs/scripts/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ def main():
parser.add_argument('--http-port', default=80, help='port to use for the HTTP scan (instead of 80)', type=int)
parser.add_argument('--https-port', default=443, help='port to use for the HTTPS scan (instead of 443)', type=int)
parser.add_argument('--path', default=argparse.SUPPRESS, help='path to scan, instead of /', type=str)
parser.add_argument(
'--no-verify', action='store_true', help='disable certificate verification in the HSTS/HPKP tests'
)
parser.add_argument('--no-verify', action='store_true', help='disable certificate verification in the HSTS tests')
parser.add_argument(
'--cookies', default=argparse.SUPPRESS, help='cookies to send in scan (json formatted)', type=json.loads
)
Expand Down
6 changes: 3 additions & 3 deletions httpobs/tests/unittests/test_grades.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
class TestGrader(TestCase):
def test_get_score_description(self):
self.assertEquals(
'Preloaded via the HTTP Public Key Pinning (HPKP) preloading process',
get_score_description('hpkp-preloaded'),
'Contribute.json implemented with the required contact information',
get_score_description('contribute-json-with-required-keys'),
)

def test_get_score_modifier(self):
self.assertEquals(0, get_score_modifier('hpkp-preloaded'))
self.assertEquals(0, get_score_modifier('contribute-json-with-required-keys'))
Loading