Header Monitoring
PCI DSS 4.0 requirement 11.6.1 asks you to detect unauthorized changes to a payment page’s security-impacting HTTP headers as well as its scripts. HeaderHawk watches headers in two ways:
| From your reports | Daily scans | |
|---|---|---|
| Plans | Every plan | The Professional plan and above |
| Headers | Content-Security-Policy and its report-only form |
Seven security headers, listed below |
| Needs | Violation reports arriving | A page marked with a scan URL |
| Sees a header removed | No | Yes |
| Sees a page with no traffic | No | Yes |
| Alerts | CSP policy changed | Security header changed, Scan grade dropped, Page scan failing |
Both alert through the same rules and destinations as violations do (How Alerting Works).
Policy changes, from your reports
Section titled “Policy changes, from your reports”Every violation report a browser sends quotes the policy that was in force on the page. HeaderHawk remembers which policies each site’s reports have quoted, and raises CSP policy changed when one it does not know turns up.
- The first day is a baseline. Every policy a site reports during its first 24 hours of reporting is learned without an alert, so adding a site does not alert on the policies it already serves.
- Memory is 30 days. A policy the site has not reported for 30 days is forgotten. If it comes back, as it would when a month-old deploy is rolled back, that is a change.
- A new policy needs evidence before it alerts: reports from two or more browser families, or 20 reports. That is the same bar a violation must clear for an immediate alert, and it is what stops one forged report from alerting anyone. Until then the policy is watched, not announced.
- A rollout is not a flood of alerts. While a deploy serves the old and new policies side by side, both are known, so alternating between them raises nothing more.
What counts as the same policy
Section titled “What counts as the same policy”Policies are compared in a normalised form, not as raw text:
- Nonces are replaced by one placeholder,
'nonce-*'. A nonce is new on every response by design, so without this a nonce-based policy would look new on every report. A policy that stops using nonces altogether has still changed. - Order of directives, and of the sources within one, does not matter.
- Case and repeats do not matter for directive names, keywords, schemes and hosts. Hashes keep their case, because base64 is case-sensitive.
- A repeated directive that the browser ignores is ignored here too.
A policy in Content-Security-Policy-Report-Only and the same policy in
Content-Security-Policy are different policies, so moving a policy from
report-only to enforced is a change.
Daily scans of the pages you mark
Section titled “Daily scans of the pages you mark”On Professional and Business, HeaderHawk scans each page you have marked on a site (Payment Pages) that has a scan URL. A payment page always has one; another kind of page is scanned when you give it one. A site can have up to 25 pages. Nothing is crawled: the scan URL is the only URL of that page HeaderHawk requests.
The scanner runs every hour and takes the pages that have not been scanned in the last day, so each page is scanned about once every 24 hours, and a page you have just added is scanned within the hour.
Each scan is the same fetch and the same grade as the free site scanner, graded with the published rubric (How scans are graded):
- one
GETof the scan URL, following up to 10 redirects, within 10 seconds; - the response headers and the initial HTML are read. The page is not rendered and no script runs, so a header added by JavaScript or a service worker is not seen;
Set-Cookievalues are removed before anything is stored;- for an HTTPS page,
hstspreload.orgis asked whether your host is on the browser HSTS preload list. The hostname is all it is sent; - on a payment page, every script the HTML names is fetched and hashed as well (below).
Redirects stay on your site
Section titled “Redirects stay on your site”A scan only goes where the site already says it lives. The scan URL, and every
redirect it follows, must be on one of the hosts the site accepts reports
from, over http or https, with no port and no credentials in the URL.
A redirect anywhere else ends the scan, and it is recorded as a failed scan
naming the host it was sent to. Addresses on private or internal networks are
refused on any host, including your own.
The headers that are watched
Section titled “The headers that are watched”Content-Security-PolicyContent-Security-Policy-Report-OnlyStrict-Transport-SecurityX-Frame-OptionsX-Content-Type-OptionsReferrer-PolicyPermissions-Policy
These are the headers that set a page’s security policy in the browser, and
the ones the rubric reads. Every other header is left out on purpose: Date, ETag, request ids
and CDN debug headers change on every response, and an alert on each of them
would teach you to ignore the ones that matter.
What counts as a change
Section titled “What counts as a change”Each scan is compared with the page’s last complete scan.
- Security header changed: a watched header was added, removed or changed. The two policy headers are compared in the normalised form above, so a new nonce is not a change. The others are compared with whitespace collapsed and case ignored. A header sent more than once is compared as the set of its values, in any order.
- Scan grade dropped: the grade is lower than the last scan’s.
- Page scan failing: the page could not be scanned three times in a row, for example because it timed out, answered with an error or a bot challenge, returned something other than HTML, or redirected off the site’s hosts. One failure alerts nobody, and a failed scan does not reset what the next complete scan is compared with.
A page’s first scan is its baseline and raises nothing.
A payment page’s scripts
Section titled “A payment page’s scripts”On a payment page, the scan also reads every <script src> in the HTML and
fetches each one, up to 25 a page, and records a SHA-256 hash of what each
served in the site’s script inventory.
This is the one part of a scan that leaves your site’s hosts: a payment page’s
scripts are mostly a payment provider’s, a tag manager’s or a CDN’s, so a
script may be fetched from any public host. A script on a private or internal
address, or named by an IP address, is never requested, and the redirect rule
for scripts is the same.
Each script is asked for as a desktop Chrome browser would ask for it, with
your page’s origin as its Origin and Referer, not as HeaderHawkMonitor.
That is deliberate: a vendor that serves different code to different clients
should serve the scan the same file your visitors run, or a pinned hash would
look changed to the scan and not to them, and a script that shows clean code
to anything that looks like a scanner is the known way to hide one. Only the
hash and what the scan found are kept, never the file.
The page itself is not run, so a script that another script adds while the page runs, such as the tags a tag manager loads, is not fetched. Browser reports are still what sees those.
Each scan’s list of scripts is also what tells HeaderHawk a script has left
the page, or moved to another host: see
Scripts that leave a page.
Only a scan that loaded the page, with a 2xx answer, counts, so a page that
starts answering with an error does not read as every script being removed.
Letting the scanner in
Section titled “Letting the scanner in”The page fetch identifies itself with this user agent:
HeaderHawkMonitor/3 (+https://docs.headerhawk.com/resources/header-monitoring/)The number is the rubric version and changes when the rubric does, so match on
HeaderHawkMonitor/. Scans come from AWS with no fixed IP address, so allow
them by user agent rather than by address. A payment page’s scripts are
requested without it, as described above, so a CDN rule that admits the scan
by user agent does not apply to them. A bot challenge, a login wall or a
401 or 403 makes the page unscannable, and after three days of that you are
alerted.
The free site scanner is separate and identifies itself differently, so your logs show your own scheduled scans apart from anyone else’s one-off scan of your site.
Where to see it
Section titled “Where to see it”Both live in the sidebar, scoped to the site chosen in the header:
- Policy → Changes lists every policy the site’s reports have quoted, in the same normalised form alerts show, one directive to a line, with when it was first and last reported and how many reports carried it. It is on every plan.
- Headers lists the pages you have marked. Open one to see its current grade, the first fix and the grade it would reach, Current headers, and its Scan history: what changed at each scan, grades that were re-baselined, and failures. Scans in a row that found nothing different share one row. A scan’s findings say what capped its grade.
Every alert links straight to the view it came from.
Both are kept for your plan’s report retention window. If you move to a plan without scans, the scan history you have stays readable and no new scans run.
What header monitoring does not decide
Section titled “What header monitoring does not decide”HeaderHawk tells you that a policy or header changed, when, and from what to what. It cannot tell whether the change was authorized. A deploy you meant and a CDN rule nobody reviewed look the same from outside, and whoever owns the site decides which it was.