← All issues

[1] LoadImageForDecoding scheme and cookie-access validation

The decode endpoint that checked a URL was valid — and nothing else.

Severity: High | Component: WebKit NetworkProcess IPC | ccf0c48

Rated High because the pre-fix endpoint accepted an attacker-controlled ResourceRequest from WebContent and validated only url.isValid(), letting a compromised renderer reach a file:// load and assert an arbitrary cookie first-party through the NetworkProcess; the escalation to actual local-file disclosure and cross-origin credentialed reads depends on downstream loader behavior the diff does not show, but the fix installs exactly the scheme and cookie-authorization checks that path requires.

LoadImageForDecoding accepted arbitrary ResourceRequest fields with only a url.isValid() check. This allowed file:// reads of NetworkProcess-sandbox files and credentialed cross-origin body reads via a spoofed firstPartyForCookies. The URL is now restricted to HTTP(S) and allowsFirstPartyForCookies is enforced, matching every other cookie-touching IPC entry point.

Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp

void NetworkConnectionToWebProcess::loadImageForDecoding(WebCore::ResourceRequest&& request, WebPageProxyIdentifier pageID, uint64_t maximumBytesFromNetwork, ...)
{
auto url = request.url();
- MESSAGE_CHECK_COMPLETION(url.isValid(), completionHandler(makeUnexpected<WebCore::ResourceError>({ })));
+ MESSAGE_CHECK_COMPLETION(url.isValid() && url.protocolIsInHTTPFamily(), completionHandler(makeUnexpected<WebCore::ResourceError>({ })));
+ MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, request.firstPartyForCookies()) == NetworkProcess::AllowCookieAccess::Allow, completionHandler(makeUnexpected<WebCore::ResourceError>({ })));

Source/WebKit/UIProcess/WebPageProxy.cpp

void WebPageProxy::loadAndDecodeImage(WebCore::ResourceRequest&& request, ...)
{
- if (isClosed() || !request.url().isValid())
+ if (isClosed() || !request.url().isValid() || !request.url().protocolIsInHTTPFamily())
return completionHandler(makeUnexpected(decodeError(request.url())));

LayoutTests/ipc/load-image-for-decoding-file-url.html

+ const fileRequest = { getRequestDataToSerialize: { variant: { m_url: { string: 'file:///private/etc/hosts' }, ... } } };
+ const definition = IPC.messages.NetworkConnectionToWebProcess_LoadImageForDecoding;
+ // asserts: 'Message check failed' && 'protocolIsInHTTPFamily'

On the NetworkProcess side, NetworkConnectionToWebProcess::loadImageForDecoding tightens its existing MESSAGE_CHECK_COMPLETION(url.isValid(), ...) to also require url.protocolIsInHTTPFamily(), and adds a second MESSAGE_CHECK_COMPLETION requiring m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, request.firstPartyForCookies()) == NetworkProcess::AllowCookieAccess::Allow. On the UIProcess side, the WebPageProxy::loadAndDecodeImage early-return guard gains || !request.url().protocolIsInHTTPFamily(). The regression test hand-crafts a ResourceRequest with a file:///private/etc/hosts URL through the IPC testing API and asserts the message is now rejected on protocolIsInHTTPFamily.

Trusting attacker-controlled ResourceRequest fields at an IPC boundary without enforcing scheme and cookie-access authorization on the sending process.

The NetworkProcess is reachable from WebContent only through IPC endpoints such as loadImageForDecoding, the privileged endpoint that fetches and decodes images on behalf of the sandboxed WebContent process. A ResourceRequest is a fully serializable bundle of load parameters (URL, firstPartyForCookies, method, headers, cookie policy) that the WebContent process constructs and sends over IPC. MESSAGE_CHECK and its completion variant validate an assertion on an incoming message; on failure they terminate or flag the connection rather than proceeding, so they are the standard authorization gate for messages arriving from a less-privileged process. protocolIsInHTTPFamily() is a predicate that is true only for http/https URLs. firstPartyForCookies is the URL used as the first-party/top-site context when deciding which cookies to attach, and allowsFirstPartyForCookies(processID, url) is the NetworkProcess policy check that confirms a given web process is permitted to use a given URL as that first party. The IPC testing API used by the regression test lets a page synthesize raw IPC messages, modeling a compromised WebContent process.

This is an IPC input-validation / access-control bypass (a confused-deputy), not a memory-safety bug. Before the fix, loadImageForDecoding accepted an entirely attacker-controlled ResourceRequest and validated only url.isValid(). Two invariants were missing. First, the request URL was not restricted to the HTTP(S) family, so a file:// URL passed validation and was handed toward the network session for loading. Second, request.firstPartyForCookies() was never checked against allowsFirstPartyForCookies, so the caller could supply an arbitrary first-party URL that the loader would trust when deciding cookie attachment. The NetworkProcess acted on request fields it trusted without re-deriving or re-authorizing them against the sending process's identity.

The exploit direction follows the regression test. From a compromised WebContent process, serialize a ResourceRequest whose m_url is file:///path/to/target and send NetworkConnectionToWebProcess_LoadImageForDecoding; pre-fix, url.isValid() passes and the scheme is not rejected at the entry point, so the request would proceed to the network session. Alternatively, set an http(s) m_url but supply an m_firstPartyForCookies for a victim origin, which pre-fix would be trusted since allowsFirstPartyForCookies was not consulted. Whether these fields then cause the local file to be read and returned as image bytes, or the victim's cookies to be attached and the body returned, depends on the loader path not shown in this diff. This is a second-stage bug: it is not reachable from unprivileged web content alone, and it does not itself grant code execution in the NetworkProcess.

This vulnerability weakens the WebContent/NetworkProcess IPC trust boundary and the cookie first-party authorization model. The security model assumes a compromised WebContent process cannot direct the NetworkProcess to load arbitrary-scheme URLs or assert an arbitrary cookie first-party; before the fix loadImageForDecoding re-emitted attacker-supplied request fields without those authorizations. An attacker who has already achieved code execution in WebContent could plausibly reach a file:// load through the NetworkProcess and could supply a spoofed firstPartyForCookies; if those fields flow to the loader as they appear to, this amounts to a local file-disclosure and cross-origin credentialed data-read primitive.

The recurring lesson: IPC endpoints that accept a full ResourceRequest and forward it to a loader are inherently confused-deputy prone. Any field the caller controls (URL scheme, firstPartyForCookies, cookie policy) must be re-authorized against the sender's identity, because the NetworkProcess's ambient authority exceeds the sender's. A url.isValid()-only gate validates syntax but not authority.

Note: The downstream loading and cookie-attachment consequences are inferred from the field names and the shape of the checks the fix adds; the network-session code that consumes these fields is not part of this diff. The entry-point validation gap itself is directly visible.