diff --git a/README.md b/README.md index 6c61279..2cd1f3c 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ ## Features -:heavy_check_mark: 55 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.
+:heavy_check_mark: 56 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.
:heavy_check_mark: 14 [checks](#checks-missing-headers) for missing security-related HTTP response headers (the ones I consider essential).
:heavy_check_mark: 1186 [checks](#checks-fingerprint-headers) for fingerprinting through HTTP response headers.
-:heavy_check_mark: 121 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.
+:heavy_check_mark: 122 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.
:heavy_check_mark: SSL/TLS checks: requires the **amazing** https://testssl.sh/.
:heavy_check_mark: Browser support references for enabled HTTP security headers: provided by https://caniuse.com/.
:heavy_check_mark: Two types of analysis: brief and detailed, along with HTTP response headers.
diff --git a/additional/insecure.txt b/additional/insecure.txt index fb347aa..f4c6d27 100644 --- a/additional/insecure.txt +++ b/additional/insecure.txt @@ -63,6 +63,7 @@ Cross-Origin-Embedder-Policy: No Valid Directives Cross-Origin-Opener-Policy: No Valid Directives Cross-Origin-Resource-Policy: No Valid Directives Digest: Deprecated Header +Document-Isolation-Policy: No Valid Directives Document-Policy: No Valid Directives Etag: Potentially Unsafe Header Expect-CT: Deprecated Header diff --git a/additional/security.txt b/additional/security.txt index cbb48ad..8370e3e 100644 --- a/additional/security.txt +++ b/additional/security.txt @@ -45,6 +45,7 @@ Cross-Origin-Embedder-Policy-Report-Only Cross-Origin-Opener-Policy Cross-Origin-Opener-Policy-Report-Only Cross-Origin-Resource-Policy +Document-Isolation-Policy Document-Policy Expect-CT Feature-Policy diff --git a/humble.py b/humble.py index d573b70..59fd6df 100644 --- a/humble.py +++ b/humble.py @@ -1923,6 +1923,9 @@ def custom_help_formatter(prog): # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy t_corp = ('cross-origin', 'same-origin', 'same-site') +# https://wicg.github.io/document-isolation-policy/ +t_doci = ('isolate-and-credentialless', 'isolate-and-require-corp', 'none') + # https://wicg.github.io/document-policy/ # https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md t_docp = ('bpp', 'document-write', 'frame-loading', 'forms', @@ -2221,23 +2224,28 @@ def custom_help_formatter(prog): if 'digest' in headers_l and '23' not in skip_list: print_details('[idig_h]', '[idig]', 'd', i_cnt) -if 'document-policy' in headers_l and '24' not in skip_list: +if 'document-isolation-policy' in headers_l and '24' not in skip_list: + doci_h = headers_l['document-isolation-policy'] + if not any(elem in doci_h for elem in t_doci): + print_details('[idocpi_h]', '[idocpi]', 'd', i_cnt) + +if 'document-policy' in headers_l and '25' not in skip_list: docp_h = headers_l['document-policy'] if not any(elem in docp_h for elem in t_docp): print_details('[idocp_h]', '[idoc]', 'm', i_cnt) -if 'etag' in headers_l and '25' not in skip_list: +if 'etag' in headers_l and '26' not in skip_list: print_details('[ieta_h]', '[ieta]', 'd', i_cnt) -if 'expect-ct' in headers_l and '26' not in skip_list: +if 'expect-ct' in headers_l and '27' not in skip_list: print_details('[iexct_h]', '[iexct]', 'm', i_cnt) if 'expires' in headers_l and any(elem in headers_l.get('cache-control', '') - for elem in t_excc) and '27' \ + for elem in t_excc) and '28' \ not in skip_list: print_details('[iexpi_h]', '[iexpi]', 'd', i_cnt) -if 'feature-policy' in headers_l and '28' not in skip_list: +if 'feature-policy' in headers_l and '29' not in skip_list: print_details('[iffea_h]', '[iffea]', 'd', i_cnt) if unsafe_scheme: @@ -2245,41 +2253,41 @@ def custom_help_formatter(prog): if ('keep-alive' in headers_l and headers_l['keep-alive'] and ('connection' not in headers_l or - headers_l['connection'] != 'keep-alive')) and '30' not in skip_list: + headers_l['connection'] != 'keep-alive')) and '31' not in skip_list: print_details('[ickeep_h]', '[ickeep]', 'd', i_cnt) -if 'large-allocation' in headers_l and '31' not in skip_list: +if 'large-allocation' in headers_l and '32' not in skip_list: print_details('[ixlalloc_h]', '[ixallocd]', 'd', i_cnt) -if 'nel' in headers_l and '32' not in skip_list: +if 'nel' in headers_l and '33' not in skip_list: nel_header = headers_l['nel'] if not any(elem in nel_header for elem in t_nel_dir): print_details('[inel_h]', '[inel]', 'd', i_cnt) if not all(elem in nel_header for elem in t_nel_req): print_details('[inelm_h]', '[inelm]', "d", i_cnt) -if 'no-vary-search' in headers_l and '33' not in skip_list: +if 'no-vary-search' in headers_l and '34' not in skip_list: nvarys_header = headers_l['no_vary-search'] if not any(elem in nvarys_header for elem in t_nvarysearch): print_details('[ifnvarys_h]', '[ifnvarys]', 'd', i_cnt) observe_brows_header = headers_l.get('observe-browsing-topics', '') if observe_brows_header and '?1' not in observe_brows_header and \ - '34' not in skip_list: + '35' not in skip_list: print_details('[iobsb_h]', '[iobsb]', 'd', i_cnt) -if 'onion-location' in headers_l and '35' not in skip_list: +if 'onion-location' in headers_l and '36' not in skip_list: print_details('[ionloc_h]', '[ionloc]', 'm', i_cnt) -if 'origin-agent-cluster' in headers_l and '36' not in skip_list: +if 'origin-agent-cluster' in headers_l and '37' not in skip_list: origin_cluster_h = headers_l['origin-agent-cluster'] if not any(elem in origin_cluster_h for elem in l_origcluster): print_details('[iorigcluster_h]', '[iorigcluster]', 'd', i_cnt) -if 'p3p' in headers_l and '37' not in skip_list: +if 'p3p' in headers_l and '38' not in skip_list: print_details('[ip3p_h]', '[ip3p]', 'd', i_cnt) -if 'permissions-policy' in headers_l and '38' not in skip_list: +if 'permissions-policy' in headers_l and '39' not in skip_list: perm_header = headers_l['permissions-policy'] if not any(elem in perm_header for elem in t_per_ft): print_details('[ifpoln_h]', '[ifpoln]', 'm', i_cnt) @@ -2296,24 +2304,24 @@ def custom_help_formatter(prog): print_detail('[ifpold]') i_cnt[0] += 1 -if 'pragma' in headers_l and '39' not in skip_list: +if 'pragma' in headers_l and '40' not in skip_list: print_details('[iprag_h]', '[iprag]', 'd', i_cnt) -if 'proxy-authenticate' in headers_l and '40' not in skip_list: +if 'proxy-authenticate' in headers_l and '41' not in skip_list: prxyauth_h = headers_l['proxy-authenticate'] if 'basic' in prxyauth_h and unsafe_scheme: print_details('[iprxauth_h]', '[ihbas]', 'd', i_cnt) if not any(elem in prxyauth_h for elem in t_proxy_auth): print_details('[iprxauthn_h]', '[iprxauthn]', 'd', i_cnt) -if 'public-key-pins' in headers_l and '41' not in skip_list: +if 'public-key-pins' in headers_l and '42' not in skip_list: print_details('[ipkp_h]', '[ipkp]', 'd', i_cnt) -if 'public-key-pins-report-only' in headers_l and '42' not in skip_list: +if 'public-key-pins-report-only' in headers_l and '43' not in skip_list: print_details('[ipkpr_h]', '[ipkp]', 'd', i_cnt) referrer_header = headers_l.get('referrer-policy', '') -if referrer_header and '43' not in skip_list: +if referrer_header and '44' not in skip_list: if not any(elem in referrer_header for elem in t_ref_secure): print_details('[iref_h]', '[iref]', 'm', i_cnt) if 'unsafe-url' in referrer_header: @@ -2322,29 +2330,29 @@ def custom_help_formatter(prog): print_details('[irefn_h]', '[irefn]', 'd', i_cnt) refresh_header = headers_l.get('refresh', '') -if refresh_header and '44' not in skip_list and \ +if refresh_header and '45' not in skip_list and \ any(elem in refresh_header for elem in t_refresh): print_details('[irefr_h]', '[irefr]', 'd', i_cnt) -if 'report-to' in headers_l and '45' not in skip_list: +if 'report-to' in headers_l and '46' not in skip_list: print_details('[irept_h]', '[irept]', 'd', i_cnt) report_h = headers_l.get('reporting-endpoints', '') -if report_h and '46' not in skip_list and HTTP_SCHEMES[0] in report_h: +if report_h and '47' not in skip_list and HTTP_SCHEMES[0] in report_h: print_details('[irepe_h]', '[irepe]', 'd', i_cnt) repdig_header = headers_l.get('repr-digest', '') -if repdig_header and '47' not in skip_list: +if repdig_header and '48' not in skip_list: if not any(elem in repdig_header for elem in t_repdig_sec): print_details('[irepdig_h]', '[irepdig]', 'd', i_cnt) if any(elem in repdig_header for elem in t_repdig_ins): print_details('[irepdigi_h]', '[irepdigi]', 'm', i_cnt) -if 'server-timing' in headers_l and '48' not in skip_list: +if 'server-timing' in headers_l and '49' not in skip_list: print_details('[itim_h]', '[itim]', 'd', i_cnt) stc_header = headers_l.get("set-cookie", '') -if stc_header and '49' not in skip_list: +if stc_header and '50' not in skip_list: if not unsafe_scheme and not all(elem in stc_header for elem in t_cookie_sec): print_details('[iset_h]', '[iset]', "d", i_cnt) @@ -2358,20 +2366,20 @@ def custom_help_formatter(prog): setlogin_header = headers_l.get("set-login", '') if setlogin_header and not any(elem in setlogin_header for elem in t_setlogin)\ - and '50' not in skip_list: + and '51' not in skip_list: print_details('[islogin_h]', '[islogin]', 'd', i_cnt) -if 'sourcemap' in headers_l and '51' not in skip_list: +if 'sourcemap' in headers_l and '52' not in skip_list: print_details('[ismap_m]', '[ismap]', 'd', i_cnt) -if 'speculation-rules' in headers_l and '52' not in skip_list: +if 'speculation-rules' in headers_l and '53' not in skip_list: print_details('[ispec_m]', '[ispec]', 'm', i_cnt) -if 'strict-dynamic' in headers_l and '53' not in skip_list: +if 'strict-dynamic' in headers_l and '54' not in skip_list: print_details('[isdyn_h]', '[isdyn]', 'd', i_cnt) sts_header = headers_l.get('strict-transport-security', '') -if sts_header and '54' not in skip_list: +if sts_header and '55' not in skip_list: try: age = int(''.join(filter(str.isdigit, sts_header))) if unsafe_scheme: @@ -2386,25 +2394,25 @@ def custom_help_formatter(prog): except ValueError: print_details('[ists_h]', '[ists]', 'm', i_cnt) -if 'supports-loading-mode' in headers_l and '55' not in skip_list: +if 'supports-loading-mode' in headers_l and '56' not in skip_list: support_mode_h = headers_l['supports-loading-mode'] if unsafe_scheme: print_details('[islmodei_h]', '[islmodei]', 'd', i_cnt) if not any(elem in support_mode_h for elem in t_support_mode): print_details('[islmode_h]', '[islmode]', 'd', i_cnt) -if 'surrogate-control' in headers_l and '56' not in skip_list: +if 'surrogate-control' in headers_l and '57' not in skip_list: surrogate_mode_h = headers_l['surrogate-control'] if not any(elem in surrogate_mode_h for elem in t_surrogate): print_details('[isurrmode_h]', '[isurrmode]', 'd', i_cnt) -if headers_l.get('timing-allow-origin', '') == '*' and '57' not in skip_list: +if headers_l.get('timing-allow-origin', '') == '*' and '58' not in skip_list: print_details('[itao_h]', '[itao]', 'd', i_cnt) -if 'tk' in headers_l and '58' not in skip_list: +if 'tk' in headers_l and '59' not in skip_list: print_details('[ixtk_h]', '[ixtkd]', 'd', i_cnt) -if 'trailer' in headers_l and '59' not in skip_list: +if 'trailer' in headers_l and '60' not in skip_list: trailer_h = headers_l['trailer'] if any(elem in trailer_h for elem in t_trailer): print_detail_r('[itrailer_h]', is_red=True) @@ -2415,47 +2423,47 @@ def custom_help_formatter(prog): print_detail('[itrailer_d_r]') i_cnt[0] += 1 -if 'transfer-encoding' in headers_l and '60' not in skip_list: +if 'transfer-encoding' in headers_l and '61' not in skip_list: transfer_h = headers_l['transfer-encoding'] if not any(elem in transfer_h for elem in t_transfer): print_details('[ictrf_h]', '[itrf]', 'd', i_cnt) -if 'vary' in headers_l and '61' not in skip_list: +if 'vary' in headers_l and '62' not in skip_list: print_details('[ixvary_h]', '[ixvary]', 'm', i_cnt) -if 'want-digest' in headers_l and '62' not in skip_list: +if 'want-digest' in headers_l and '63' not in skip_list: print_details('[ixwandig_h]', '[ixwandig]', 'd', i_cnt) -if 'warning' in headers_l and '63' not in skip_list: +if 'warning' in headers_l and '64' not in skip_list: print_details('[ixwar_h]', '[ixward]', 'd', i_cnt) wwwa_header = headers_l.get('www-authenticate', '') -if wwwa_header and unsafe_scheme and ('basic' in wwwa_header) and '64' not in \ +if wwwa_header and unsafe_scheme and ('basic' in wwwa_header) and '65' not in \ skip_list: print_details('[ihbas_h]', '[ihbas]', 'd', i_cnt) -if 'x-content-security-policy' in headers_l and '65' not in skip_list: +if 'x-content-security-policy' in headers_l and '66' not in skip_list: print_details('[ixcsp_h]', '[ixcsp]', 'd', i_cnt) -if 'x-content-security-policy-report-only' in headers_l and '66' not in \ +if 'x-content-security-policy-report-only' in headers_l and '67' not in \ skip_list: print_details('[ixcspr_h]', '[ixcspr]', 'd', i_cnt) -if 'x-content-type-options' in headers_l and '67' not in skip_list: +if 'x-content-type-options' in headers_l and '68' not in skip_list: if ',' in headers_l['x-content-type-options']: print_details('[ictpd_h]', '[ictpd]', 'd', i_cnt) elif 'nosniff' not in headers_l['x-content-type-options']: print_details('[ictp_h]', '[ictp]', 'd', i_cnt) -if headers_l.get('x-dns-prefetch-control', '') == 'on' and '68' not in \ +if headers_l.get('x-dns-prefetch-control', '') == 'on' and '69' not in \ skip_list: print_details('[ixdp_h]', '[ixdp]', 'd', i_cnt) -if 'x-download-options' in headers_l and '69' not in skip_list: +if 'x-download-options' in headers_l and '70' not in skip_list: print_details('[ixdow_h]', '[ixdow]', 'm', i_cnt) xfo_header = headers_l.get('x-frame-options', '') -if xfo_header and '70' not in skip_list: +if xfo_header and '71' not in skip_list: if ',' in xfo_header: print_details('[ixfo_h]', '[ixfo]', 'm', i_cnt) if 'allow-from' in xfo_header: @@ -2463,11 +2471,11 @@ def custom_help_formatter(prog): if xfo_header not in t_xfo_dir: print_details('[ixfoi_h]', '[ixfodi]', 'm', i_cnt) -if 'x-pad' in headers_l and '71' not in skip_list: +if 'x-pad' in headers_l and '72' not in skip_list: print_details('[ixpad_h]', '[ixpad]', 'd', i_cnt) permcross_header = headers_l.get('x-permitted-cross-domain-policies', '') -if permcross_header and '72' not in skip_list: +if permcross_header and '73' not in skip_list: if not any(elem in permcross_header for elem in t_permcross): print_details('[ixpermcross_h]', '[ixpermcross]', 'm', i_cnt) if 'all' in permcross_header: @@ -2475,24 +2483,24 @@ def custom_help_formatter(prog): if ',' in permcross_header: print_details('[ixpermcrossd_h]', '[ixpermcrossd]', 'm', i_cnt) -if headers_l.get('x-pingback', '').endswith('xmlrpc.php') and '73' not in \ +if headers_l.get('x-pingback', '').endswith('xmlrpc.php') and '74' not in \ skip_list: print_details('[ixpb_h]', '[ixpb]', 'd', i_cnt) robots_header = headers_l.get('x-robots-tag', '') -if robots_header and '74' not in skip_list: +if robots_header and '75' not in skip_list: if not any(elem in robots_header for elem in t_robots): print_details('[ixrobv_h]', '[ixrobv]', 'm', i_cnt) if 'all' in robots_header: print_details('[ixrob_h]', '[ixrob]', 'm', i_cnt) -if 'x-runtime' in headers_l and '75' not in skip_list: +if 'x-runtime' in headers_l and '76' not in skip_list: print_details('[ixrun_h]', '[ixrun]', 'd', i_cnt) -if 'x-sourcemap' in headers_l and '76' not in skip_list: +if 'x-sourcemap' in headers_l and '77' not in skip_list: print_details('[ixsrc_h]', '[ixsrc]', 'd', i_cnt) -if 'x-ua-compatible' in headers_l and '77' not in skip_list: +if 'x-ua-compatible' in headers_l and '78' not in skip_list: print_details('[ixuacom_h]', '[ixuacom]', 'm', i_cnt) if http_equiv: @@ -2501,13 +2509,13 @@ def custom_help_formatter(prog): in tuple): print_details('[ixuameta_h]', '[ixuameta]', 'd', i_cnt) -if 'x-webkit-csp' in headers_l and '78' not in skip_list: +if 'x-webkit-csp' in headers_l and '79' not in skip_list: print_details('[ixwcsp_h]', '[ixcsp]', 'd', i_cnt) -if 'x-webkit-csp-report-only' in headers_l and '79' not in skip_list: +if 'x-webkit-csp-report-only' in headers_l and '80' not in skip_list: print_details('[ixwcspr_h]', '[ixcspr]', 'd', i_cnt) -if 'x-xss-protection' in headers_l and '80' not in skip_list: +if 'x-xss-protection' in headers_l and '81' not in skip_list: print_details('[ixxpdp_h]', '[ixxpdp]', 'm', i_cnt) if '0' not in headers_l['x-xss-protection']: print_details('[ixxp_h]', '[ixxp]', 'm', i_cnt) diff --git a/l10n/details.txt b/l10n/details.txt index 5b195db..fe23808 100644 --- a/l10n/details.txt +++ b/l10n/details.txt @@ -300,6 +300,13 @@ Ref: https://wicg.github.io/document-policy/ Ref: https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md +[idocpi_h] + Document-Isolation-Policy (No Valid Directives) + +[idocpi] + Include at least one valid directive. + Ref: https://wicg.github.io/document-isolation-policy/ + [ixach_h] Accept-CH (Ignored Header) diff --git a/l10n/details_es.txt b/l10n/details_es.txt index b6b89db..386555c 100644 --- a/l10n/details_es.txt +++ b/l10n/details_es.txt @@ -301,6 +301,13 @@ Ref: https://wicg.github.io/document-policy/ Ref: https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md +[idocpi_h] + Document-Isolation-Policy (Sin directivas adecuadas) + +[idocpi] + Incluya, al menos, una directiva correcta. + Ref: https://wicg.github.io/document-isolation-policy/ + [ixachd_h] Accept-CH (Valores obsoletos)