CVE-2024-44296
Overview
Background
- Content Security Policy (CSP)
- A per-document policy that restricts which origins the page may load or connect to, enforced by the browser to limit injection and exfiltration.
- connect-src directive
- The CSP directive controlling script-initiated connections such as fetch, XHR, WebSocket, EventSource, and beacon/ping requests.
- Hyperlink auditing ping
- The ping attribute on <a> elements causes the browser to send a POST notification to the listed URL(s) when the link is followed.
- PingLoader::sendPing
- The WebCore routine that constructs and dispatches ping/auditing POST requests on behalf of a frame’s document.
- allowConnectToSource
- The ContentSecurityPolicy method that returns whether a given URL is permitted by connect-src, emitting a refusal console message when it is not.
Root Cause Analysis
The patch modifies PingLoader::sendPing() in Source/WebCore/loader/PingLoader.cpp, the code path that issues hyperlink auditing pings (the ping attribute on anchors, and related beacon-style POSTs).
Before the fix, sendPing() obtained the document and immediately called upgradeInsecureRequestIfNeeded() and built the POST request without ever consulting the document’s Content Security Policy about whether the ping destination was an allowed connection target.
The fix inserts, right after Ref document = *frame.document(), a guard: if (!document->checkedContentSecurityPolicy()->allowConnectToSource(pingURL)) return;. The violated invariant is that network requests initiated on behalf of a document must be vetted against that document’s CSP, and specifically that ping/auditing requests fall under the connect-src directive (the same directive governing fetch/XHR/WebSocket/beacon connections). Because sendPing() skipped allowConnectToSource(), a page could send a ping to a host that its own connect-src policy forbade, so CSP was not enforced for this request type. The added layout tests confirm the semantics precisely: connect-src-ping-allowed.html declares connect-src including localhost:8443 and expects the ping to proceed, while connect-src-ping-blocked.html omits localhost:8443 and expects the console message ‘Refused to connect to https://localhost:8443/ because it does not appear in the connect-src directive of the Content Security Policy.’
The fix restores the invariant by consulting CSP and returning early (silently dropping the ping) when the destination is not permitted, matching how other connect-src-governed loads are gated.
Attack Path
- Author a page under a restrictive CSP The attacker (or attacker-controlled content on a site) operates in a document whose Content-Security-Policy sets a connect-src that is intended to prevent connections to certain hosts.
- Emit a ping to a forbidden host Place an anchor with a ping attribute (or otherwise trigger sendPing) targeting a URL not permitted by connect-src, e.g. <a href=… ping=‘https://forbidden-host’>.
- Trigger the navigation/click Cause the ping to fire (user click or scripted click()), reaching PingLoader::sendPing().
- Bypass the policy Because sendPing() did not call allowConnectToSource, the POST ping is sent to the forbidden host despite connect-src, exfiltrating a beacon/notification the policy was meant to block.
- Abuse the covert channel Use the un-vetted ping as a data exfiltration or tracking channel that CSP was supposed to close; this is a policy-enforcement bypass, not memory corruption.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
PingLoader::sendPingSource/WebCore/loader/PingLoader.cpp |
modified | Adds an early-return CSP check calling checkedContentSecurityPolicy()->allowConnectToSource(pingURL) so ping requests are gated by connect-src like other connections; previously the check was absent. |
connect-src-ping-allowed testLayoutTests/http/tests/security/contentSecurityPolicy/connect-src-ping-allowed.html |
added | Verifies a ping to a host present in connect-src is permitted. |
connect-src-ping-blocked testLayoutTests/http/tests/security/contentSecurityPolicy/connect-src-ping-blocked.html |
added | Verifies a ping to a host absent from connect-src is refused with a CSP console message. |
sample resource / expected resultsLayoutTests/http/tests/security/contentSecurityPolicy/resources/sample.html (+ *-expected.txt) |
added | Supporting navigation target and expected-output baselines for the allowed/blocked tests. |
Audit Directions
- Audit PingLoader's other sendersIn PingLoader.cpp check sendViolationReport, sendBeacon-related paths, and any other request-emitting function for a matching allowConnectToSource / CSP check; grep sendPing, sendViolationReport, allowConnectToSource in that file.
- Enumerate all connect-src sinksGrep across Source/WebCore for network requests that should be connect-src-governed (fetch, XHR, WebSocket, EventSource, beacon, ping, report-uri/report-to) and confirm each calls allowConnectToSource before dispatch.
- Find loaders that skip CSP entirelySearch loader code for request construction that calls upgradeInsecureRequestIfNeeded or ResourceLoadNotifier without a preceding CSP allow* call, a tell that a request sink was added without policy gating.
- Check other CSP directives for missing sinksApply the same audit to img-src, media-src, frame-src, and form-action by grepping the corresponding allow* methods (allowImageFromSource, allowFormAction, etc.) against the code paths that actually initiate those loads.