← All issues

[1] NetworkProcess origin-allowlist IPC gating

Severity: High | Component: WebKit NetworkProcess | 7143ace

Rated High because the diff confirms three IPC messages on NetworkConnectionToWebProcess were always dispatchable from any WebContent process and mutated the process-global origin access allowlist consulted by CORS/SOP; the included regression test demonstrates that a compromised renderer turns a same-origin XHR check into a cross-origin data read, which is a direct universal-CORS-bypass primitive on the Network process trust boundary.

Origin access allowlist IPC messages on NetworkConnectionToWebProcess modify a process-global allowlist with no validation, allowing a compromised WebContent process to bypass CORS for all connections. These messages are only used by TestRunner SPI. Gate them behind EnabledBy=AllowTestOnlyIPC so they are rejected unless the test-only flag is set.

Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in

- AddOriginAccessAllowListEntry(String sourceOrigin, String destinationProtocol, String destinationHost, bool allowDestinationSubdomains);
- RemoveOriginAccessAllowListEntry(String sourceOrigin, String destinationProtocol, String destinationHost, bool allowDestinationSubdomains);
- ResetOriginAccessAllowLists();
+ [EnabledBy=AllowTestOnlyOriginAccessAllowListIPC] AddOriginAccessAllowListEntry(String sourceOrigin, String destinationProtocol, String destinationHost, bool allowDestinationSubdomains);
+ [EnabledBy=AllowTestOnlyOriginAccessAllowListIPC] RemoveOriginAccessAllowListEntry(String sourceOrigin, String destinationProtocol, String destinationHost, bool allowDestinationSubdomains);
+ [EnabledBy=AllowTestOnlyOriginAccessAllowListIPC] ResetOriginAccessAllowLists();

Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml

+AllowTestOnlyOriginAccessAllowListIPC:
+ type: bool
+ status: embedder
+ exposed: [ WebKit ]
+ webcoreBinding: none
+ defaultValue:
+ WebKit:
+ default: false
+ sharedPreferenceForWebProcess: true

Tools/TestWebKitAPI/Tests/WebKit/WKWebView/IPCTestingAPI.mm

+ [webViewA stringByEvaluatingJavaScript:[NSString stringWithFormat:
+ @"IPC.sendMessage('Networking', 0,"
+ " IPC.messages.NetworkConnectionToWebProcess_AddOriginAccessAllowListEntry.name,"
+ " ["
+ " { type: 'String', value: 'http://localhost:%u' },"
+ " { type: 'String', value: 'http' },"
+ " { type: 'String', value: '127.0.0.1' },"
+ " { type: 'bool', value: 1 }"
+ " ]"
+ ")", serverPort]];
+ EXPECT_WK_STREQ(result, "FETCHED:cross-origin-data");

The patch tags three IPC messages — AddOriginAccessAllowListEntry, RemoveOriginAccessAllowListEntry, and ResetOriginAccessAllowLists — with [EnabledBy=AllowTestOnlyOriginAccessAllowListIPC] in NetworkConnectionToWebProcess.messages.in. The corresponding preference is introduced in UnifiedWebPreferences.yaml with sharedPreferenceForWebProcess: true and default: false, then wired into WebKitTestRunner (TestOptions.cpp, TestOptions.h, TestController.cpp) so layout tests can opt in. Two regression tests (AddOriginAccessAllowListEntryRequiresTestOnlyIPC and AddOriginAccessAllowListEntryAllowedWithTestOnlyIPC) drive the IPC via the IPC testing API across two WKWebViews sharing a process pool and demonstrate the bypass is reachable when the flag is off and blocked when it is.

Test-only IPC entry point exposed on the production renderer-to-network surface, allowing a compromised WebContent process to mutate process-global same-origin/CORS policy without authorization.

WebKit splits the browser into multiple processes; WebContent runs untrusted web content, and NetworkProcess performs all network I/O on its behalf, communicating via Mach IPC. NetworkConnectionToWebProcess is the per-WebContent endpoint inside NetworkProcess; the messages it accepts are declared in NetworkConnectionToWebProcess.messages.in and dispatched by code generated from that file.

The [EnabledBy=Pref] annotation on a message declaration tells the generator to inject a runtime check against the named preference — when the preference is false, the message is rejected before its handler runs. sharedPreferenceForWebProcess: true causes the preference value to be propagated from the UI process to the WebContent and Network processes so the generated check can see it.

The origin access allowlist (SecurityOrigin::addOriginAccessAllowlistEntry and its OriginAccessPatterns storage) is a process-global table of (source, protocol, host, subdomains) tuples; same-origin and CORS checks consult it, and a matching entry causes a cross-origin access to be treated as same-origin. The TestRunner SPI exposes a function so layout tests can populate this allowlist to simulate cross-origin scenarios — that is the API's only intended consumer.

The bug is a missing IPC authorization check. Before the fix, the three origin-allowlist messages on NetworkConnectionToWebProcess were always dispatchable from any WebContent process with no validation. Their handlers mutate the process-global origin allowlist that SecurityOrigin::canAccess and the CORS path consult when deciding whether a cross-origin network access is permitted. The allowlist API exists only to support TestRunner SPI, so its presence on a production IPC surface is a trust-boundary violation: a compromised WebContent process could append an entry of its choosing, and from that point onward any page loaded by any WebContent process sharing that Network process is exempted from CORS for the chosen destination.

The included regression test demonstrates the exact primitive. Page A on 127.0.0.1 injects an allowlist entry whitelisting localhost127.0.0.1, then page B on localhost performs xhr.open('GET', 'http://127.0.0.1:.../target'). With the flag off the assertion is BLOCKED:; with the flag on it is FETCHED:cross-origin-data. The same primitive turns post-renderer-compromise into a credentialed cross-origin read against arbitrary HTTP(S) origins: an attacker would send AddOriginAccessAllowListEntry with sourceOrigin=http://attacker.example, destinationProtocol=http, destinationHost=victim.example, allowDestinationSubdomains=true, then fetch victim.example from any attacker page and read the cookie-bearing response body.

The cross-process security model assumes that a compromised WebContent process is still constrained by the Network process to honour CORS for cross-origin reads — the Network process is the policy enforcement point a compromised renderer cannot talk past. This vulnerability weakens exactly that boundary. The allowlist is process-global, so the bypass also affects every other WebContent process sharing the same Network process for the lifetime of the allowlist (or until ResetOriginAccessAllowLists is sent).

The choice to gate behind a new preference (AllowTestOnlyOriginAccessAllowListIPC) rather than the existing umbrella AllowTestOnlyIPC is deliberate: tests that need the allowlist API can be enabled in isolation without flipping the broader test-only IPC switch, which keeps the additional risk surface minimised for the rest of the test suite.