Skip to content

HSTS preload list check against API#1809

Closed
tosticated wants to merge 1 commit into
testssl:3.2from
tosticated:hsts_preloadlist
Closed

HSTS preload list check against API#1809
tosticated wants to merge 1 commit into
testssl:3.2from
tosticated:hsts_preloadlist

Conversation

@tosticated

Copy link
Copy Markdown
Contributor

First attempt at addressing #1248: adding support for HSTS preload list lookups.

  • Used the API example as provided in HSTS Browser Preloading Check ( Like SSL Labs ) #1248. This seems to be the list that other lists (Firefox, Edge, etc.) are based on. A browser may still have an older copy of the list, but the state on hstspreload.org should propagate eventually.
  • The 'bulk' variable seems to be used for any entry that is added via the form on hstspreload.org, as opposed to any manually added domains such as mail.google.com for example (does not respond with preload header, but is in fact included in the list).
  • Of course only works when --phone-out is being set.

Feedback is welcome. Especially regarding the way the results are displayed. The idea is that the check is done even when the HSTS preload header is missing. This would show unintended and/or undesired situations such as having a list addition rejected because the 'preload' header itself is missing. Based on my understanding of how the list should work, I have currently assigned the following:

hsts_implementation_1
Explanation of some columns:

  • has preload header: indicates the response of the server that testssl.sh is testing
  • preloadedDomain == name: True if testssl.sh is currently testing the 'responsible domain'. False if it is a subdomain. A subdomain may not have 'preloaded' returned, but still can have a status like 'preloaded' according to the API.
  • bulk: if the API 'bulk' variable was true or false. When irrelevant for testssl.sh severity: "N/A".

As I was writing this check, it grew from just a simple 'status' to this entire 'lookup table'. If this is too much, it can also be changed back to just viewing the status without any severity and advice.

@drwetter

Copy link
Copy Markdown
Collaborator

Sorry, about the late reply. First: much appreciated, thanks!

I read and tried that in December. I can't get to it and need a few days for a consolidate answer!

Comment thread testssl.sh
[[ -z "$key" ]] && return 0

# Check if key exists in response. If not, the API may have changed or something.
! grep -Fiaqw "$key" $tmpfile && debugme echo "HSTS preloadlist key unrecognized: $key" && return 21

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This grep is legacy code. Can't this be changed into something like [[ "$tmpfile" =~ \ ${key}\ ]] ? (please doublecheck)

Comment thread testssl.sh
! grep -Fiaqw "$key" $tmpfile && debugme echo "HSTS preloadlist key unrecognized: $key" && return 21

# Check if there is a match, return 10 if there is, 20 if not
grep -Fiaqw "\"$key\": $value" $tmpfile && return 10 || return 20

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above wrt legacy

Comment thread testssl.sh
out "$indent"; pr_bold " HSTS preload list "

# Check if the domain is also the preloadedDomain. If so, it *may* be correct that the server response header does not have 'preloaded' included.
check_hsts_preloadlist_match "$NODE" "preloadedDomain" "\"$NODE\""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason you use here and other places string why you pass the double quotes here? If they are needed at all I would rather handle this in the function and not in the call.

Comment thread testssl.sh
debugme echo "Temporary lookupvariable: $preloadcombined"

# Determine and show the outcome
case "$(check_hsts_preloadlist_value "$NODE" "status")" in

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for a plain string there should be no need to enclose it in quotes

Comment thread testssl.sh
case $key in
"status") values=("\"unknown\"" "\"pending\"" "\"rejected\"" "\"preloaded\"") ;;
"bulk") values=("true" "false") ;;
*) return 1 ;; # No supported key provided.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please double check here the usage of quotes. I believe for status and bulk they can be removed

For the array values I am not sure whether they need contain quotes.

Comment thread testssl.sh
check_hsts_preloadlist_value() {
local domain="$1"
local key="$2"
local values

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

local -a value=() would declare the type better.

@drwetter

drwetter commented Feb 4, 2021

Copy link
Copy Markdown
Collaborator

Good work! You spend a lot of time for the lookup table and the decision table you provided is quite impressive.

The information on the screen for both easy cases (HSTS and no preload anywhere, HSTS and preload eerywhere)

image

I miss use case to test every variant of this. If I sleep over it I probably can test 1-2 more than the basic variants. If you could address the minor points in the code; I'll do a few more tests and then it should be set.

Thanks for your work!

@drwetter drwetter added the 3.3dev next dev label Jan 27, 2025
@drwetter drwetter marked this pull request as draft July 17, 2025 11:17
@drwetter drwetter added the waiting for more input User needs to give more information label Jul 17, 2025
@potato-20

Copy link
Copy Markdown
Contributor

@drwetter @tosticated#1248 and this PR are still labeled important/3.3dev, so I'd like to help get it over the line.

@tosticated, thanks for the original implementation and the decision table — full credit to you; if you're not actively working on it, I'm happy to revive it as a fresh PR against 3.3dev and keep your credit.

My plan: rebase onto current 3.3dev and address the review comments here —

  • replace the legacy grep -Fiaqw with [[ ... =~ ... ]],
  • move the JSON quoting into check_hsts_preloadlist_match() so callers pass plain status/bulk/domain values,
  • use local -a for the value arrays.

To resolve what stalled this (verifying every variant of the preload decision table), I'd add a t/ unit test modelled on t/52_ocsp_revoked.t that exercises the --phone-out lookup against a known-preloaded and a known-unlisted domain.

@drwetter — before I finalize: are you happy with the existing decision-table approach (status × header-marked / same-domain / bulk), or would you prefer a simpler status-only output? Happy to build to whichever you prefer.

@drwetter

drwetter commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

@potato-20 : would be great! Allow me some time for feedback

@drwetter

Copy link
Copy Markdown
Collaborator

@potato-20

Before I finalize: are you happy with the existing decision-table approach (status × header-marked / same-domain / bulk), or would you prefer a simpler status-only output? Happy to build to whichever you prefer.

It looked fine like it was to me, see picture .

And: unit tests are always appreciated ;-)

@potato-20

Copy link
Copy Markdown
Contributor

I've opened #3060 which revives this onto current 3.3dev and addresses @drwetter's review comments here (pure-bash matching, quoting handled inside the helper, local -a, kept the decision table) plus adds a unit test (t/53_hsts_preload.t). Full credit to @tosticated for the original implementation — retained in CREDITS.md. This draft can probably be closed in favour of #3060.

@drwetter

Copy link
Copy Markdown
Collaborator

Cool, thanks a lot! I'll get to it next week.

@drwetter drwetter closed this Jun 12, 2026
potato-20 added a commit to potato-20/testssl.sh that referenced this pull request Jun 17, 2026
Revives and rebases testssl#1809 by @tosticated (Jim Blankendaal) onto 3.3dev. When --phone-out is set, run_hsts now queries https://hstspreload.org/api/v2/status and reports whether the domain is on the browser HSTS preload list (preloaded/pending/rejected/unknown), cross-referenced with the served header, the same-domain check and the bulk flag.

Addresses the review comments on testssl#1809: the API-response matching uses native bash string matching instead of forking grep, the JSON quoting is handled inside check_hsts_preloadlist_match() so callers pass plain values, and the value arrays use 'local -a'. The output decision table is kept as-is (per maintainer feedback). Adds t/53_hsts_preload.t. Original design and decision table by @tosticated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.3dev next dev important waiting for more input User needs to give more information

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants