Why CSP reports fill up with browser extension and bot noise
Browser extensions, crawlers and data: URIs trigger CSP violation reports your site didn't cause. How HeaderHawk identifies that noise and keeps it out of your alerts.
Michael Cameron4 min readUpdated
Turn on Content Security Policy reporting and many of the first reports aren’t
about your site at all. A browser extension injects a script, a search crawler
renders the page, or something on the page loads an image from a data: URI,
and the browser reports each one as a violation. They look just like the reports
you need to act on.
HeaderHawk labels those reports as noise when they arrive. They stay out of your alerts, the clear-cut ones stay out of your issues list, and every one of them is still stored if you want to look.
Where CSP report noise comes from
Browser extensions run their own scripts inside your pages. When your policy
blocks one, the report’s blocked URI starts with the extension’s scheme, such as
chrome-extension:// or moz-extension://, instead of a URL on your site or a
service you use.
Crawlers, link previews and automation tools load your pages too. Search engines render them, chat apps fetch them to build a preview, and headless browsers and test scripts run against them. Any of those visits can send violation reports.
about:, data: and blob: URIs appear when something creates content inside
the browser rather than loading it from a server, and a policy that doesn’t list
those schemes reports them.
How HeaderHawk identifies noise
Each report is classified once, after the raw report has been stored, using the blocked URI, the directive that blocked it and the user agent of the browser that sent the report. Three rules mark a report as known noise, and the first match decides.
- The blocked URI starts with a browser extension scheme, such as
chrome-extension://,moz-extension://,safari-web-extension://,webkit-masked-url://orms-browser-extension://. - The blocked URI is an
about:URI, or adata:orblob:URI that was not blocked as a script. - The user agent belongs to a known crawler, link-preview fetcher, headless browser, test driver or HTTP library, such as Googlebot, Bingbot, HeadlessChrome, Puppeteer, Playwright or curl.
A data: or blob: URI blocked as a script (by script-src,
script-src-elem, script-src-attr or worker-src) is
likely noise instead. Extensions and in-page tooling load scripts that way all
the time, but so does an attacker who has found a way to inject one, so it is
not hidden with the rest.
A report that matches none of the rules is signal. The rules are fixed lists, with no scoring and no machine learning, so the same report is classified the same way every time.
| Blocked URI | Directive | User agent | Classified as |
|---|---|---|---|
chrome-extension://abc/inject.js |
any | any | known noise |
data:image/png;base64,… |
img-src |
any | known noise |
blob:https://example.com/uuid |
script-src-elem |
desktop Chrome 120 | likely noise |
https://cdn.example.com/script.js |
script-src-elem |
Mozilla/5.0 (compatible; HeadlessChrome/90.0) |
known noise |
https://cdn.example.com/script.js |
script-src-elem |
desktop Chrome 120 | signal |
inline |
script-src-elem |
desktop Chrome 120 | signal |
For what a real script violation contains, the cross-site scripting guide walks through one.
What happens to noise reports
Nothing is deleted. Noise reports are stored and kept for as long as any other report, and the reports explorer shows every report however it was classified.
The issues list hides known noise by default. Turn on Show noise to bring those issues back. Likely noise is never hidden: its issues stay in the list, dimmed and marked Noise.
How noise affects CSP alerts
HeaderHawk groups violations by signature into 15-minute windows before deciding whether to alert. Known noise is left out before a window is counted, so an extension that fires on thousands of page views can’t set off an alert. Likely noise is counted but is not signal, so a window of nothing else alerts nobody.
A window sends a real-time alert when two or more browser families reported the same violation, or when twenty or more signal reports arrived from one. Anything below that can go to a daily or weekly digest instead, and the digest leaves out issues with no signal reports, which covers both kinds of noise. The features page shows how issues, alerts and digests fit together.
Why HeaderHawk uses simple rules
A report marked as noise is one you won’t see unless you look for it, so the reason should be something you can check. Every noise decision comes from one of the rules above, and every noise report is still there when you turn noise on.