After seeing this exact same pattern in some of my applications, I finally got to the root of this!
The weirdness we were seeing was that CSP reports were coming in for a hostname which was definitely in the script-src
directive; and we know that script-src-elem
is supposed to fall back to those directives. From that perspective, it should have been literally impossible for these reports to happen.
Here's what we found: the users these reports were coming from were using the PrivacyBadger browser extension, which was leading to false positive CSP reports for the hosts (Google) that it blocked. I didn't dig too far into it, but here's my theory on how that happens:
- The Content Security Policy performs a pre-request check for the JavaScript include on the page (eg. gstatic.com or google-analytics.com). The pre-request check passes, because the hostname is allowed in the policy.
- The browser initiates a request for the resource
- PrivacyBadger intercepts the request via the browser's onBeforeRequest API (see PrivacyBadger source and Chrome documentation)
- ProvacyBadger returns a surrogate data blob for the asset. It does this to ensure that code which relies on the real javascript (eg.
window.ga
) won't break.
- The browser then performs a post-request check against the returned base64 blob
- The post-request check fails - because the policy does not allow
data:
for script-src
- The browser sends a CSP report for the blocked asset.
This seems like it might be a browser bug - because the report reflects the original asset's third party hostname; while the blocked content is actually a data:
blob that was returned via the intercepted request.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…