← All reports

Local Network Access check lands in NetworkProcess

Component: WebKit NetworkProcess / Local Network Access | 944f82d

Local Network Access (LNA), the successor to the older CORS-RFC1918 proposal, restricts requests originating in a more-public address space — an ordinary public website — from reaching a less-public one such as a private LAN or loopback, unless the user grants permission. It is similar in spirit to mixed-content blocking or CORS: a comparison performed before the load proceeds, not a transport-level restriction.

This commit adds WebKit's implementation as two new WebCore functions, localNetworkAccessPermissionRequestOutcome() and performLocalNetworkAccessCheck(), and wires them into NetworkResourceLoader so subresource responses, redirects, and cache hits are all gated by an address-space comparison followed by an asynchronous permission decision. There is no prompting UI yet: NetworkSession::requestLocalNetworkAccessPermission() currently resolves synchronously, and in practice only ever returns Denied — either for an undetermined address space or as a refusal derived from the Prompt outcome — because the grant store and the unpromptable paths have no way to be reached yet.

  NetworkResourceLoader
    ├─ response received ─┐
    ├─ redirect ──────────┼─► performLocalNetworkAccessCheck()
    └─ cache hit ─────────┘      ├─ compare client address space
                                 │    vs. target address space
                                 └─► localNetworkAccessPermissionRequestOutcome()
                                       └─ NetworkSession::requestLocalNetworkAccessPermission()
                                            (synchronous today; Denied in practice)

This is a brand-new security boundary meant to stop public web pages from silently reaching into a user's local network — routers, IoT devices, dev servers. It currently covers only subresource loads; iframe navigations, main-resource loads, and permissions-policy enforcement are explicitly left as FIXMEs during rollout, so the boundary is real but partial.

The trust model deserves attention. The check runs in NetworkProcess, but two of its critical inputs — the client's address space and whether the client is a secure context — are still reported by the WebProcess rather than derived independently, because full policy container inheritance is not implemented yet. NetworkProcess normally treats the WebProcess as semi-trusted only for non-security-critical data, so a security gate whose inputs come from the renderer is a known-temporary but security-relevant trust decision, tracked as a follow-up bug.

Narrow: enumerate the load paths that do not yet route through performLocalNetworkAccessCheck() — iframe navigations, main-resource loads — because during rollout those are the supported way to reach a local network target, and the match tell is any NetworkResourceLoader entry point without a call into the new check. Wider: the portable pattern is a security decision made in a privileged process from values the sandboxed process supplied; wherever NetworkProcess consumes renderer-reported provenance (address space, secure-context flag, origin claims) to gate rather than merely to log, the same question applies — audit the other NetworkSession permission and policy hooks for renderer-sourced inputs, and the review tell is a field arriving in a NetworkResourceLoadParameters-shaped struct that feeds a branch rather than a header. Widest: every security check introduced with a stubbed decision function is a boundary whose real behavior has not been exercised; the general hunt is for permission APIs that currently resolve synchronously with a fixed outcome, since the asynchronous, grant-store-backed version will run under entirely different re-entrancy and lifetime conditions than anything tested today.