Local Network Access

Unofficial Proposal Draft,

More details about this document
This version:
https://wicg.github.io/local-network-access/
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

Restrict access to the users' local network with a new permission

Status of this document

1. Introduction

This section is not normative.

Although [RFC1918] has specified a distinction between "private" and "public" internet addresses for over two decades, user agents haven’t made much progress at segregating the one from the other. Websites on the public internet can make requests to local devices and servers, which enable a number of malicious behaviors, including attacks on users' routers like those documented in [DRIVE-BY-PHARMING], [SOHO-PHARMING] and [CSRF-EXPLOIT-KIT].

Local Network Access aims to prevent these undesired requests to insecure devices on the local network. This is achieved by deprecating direct access to local IP addresses from public websites, and instead requiring that the user grants permission to the initiating website to make connections to their local network.

Note: This proposal builds on top of Chrome’s previously paused [PRIVATE-NETWORK-ACCESS] work but differs by gating access on a permission rather than via preflight requests.

1.1. Goals

The overarching goal is to prevent the user agent from inadvertently enabling attacks on devices running on a user’s local intranet, or services running on the user’s machine directly. For example, we wish to mitigate attacks on:

There should be a well-lit path to allow these requests when the user is both expecting and explicitly allowing the local network access requests to occur. For example, a user logged in to plex.tv may want to allow the site to connect to their local media server to directly load media content over the local network instead of routing through remote servers. See S1.2 below for more examples.

1.2. Non-goals

This spec does not attempt to make it easier to use HTTPS connections on local network devices. While this would be a useful goal, solving this problem is out of scope for this specification

1.3. Examples

1.3.1. User granting permission

Alice is at home on her laptop browsing the internet. She has a printer on her local network built by Acme Printing Company that is running a simple HTTP server. Alice is having a problem with the printer not properly functioning.

Alice goes to Acme Printing Company’s web site to help diagnose the problem. Acme Printing Company’s web site tells Alice that it can connect to the printer to look at the diagnostic output of the printer. Alice’s browser asks Alice to allow https://support.acmeprintingcompany.com to connect to local devices on her network. Alice grants permission for https://support.acmeprintingcompany.com to connect to local devices on her network, and https://support.acmeprintingcompany.com connects to her local printer’s diagnostic output, and tells Alice that a part is malfunctioning on the printer and needs to be replaced.

1.3.2. User denying permission

Alice continues browsing online to find the best price for the replacement part on her printer. While looking at a general tech support forum, she suddenly gets a permission request in her browser for https://printersupport.evil.com to connect to local devices on her local network. Being suspicious of why https://printersupport.evil.com would need to connect to local devices, she denies the permission request.

1.3.3. New device configuration

Instead of replacing the part on the printer, Alice decides instead to buy a new printer from Beta Manufacturing. Upon plugging in the printer and connecting it to her local network, Alice follows the instructions and goes to https://setup.betaprinters.com on her laptop. Upon opening the site, she sees a button that will help her set up the printer defaults. Hitting the button, she gets a permission prompt asking for permission for https://setup.betaprinters.com to connect to her local devices, which she accepts.

2. Framework

2.1. IP Address Space

Define IPAddressSpace as follows:

enum IPAddressSpace { "public", "local" };

Every IP address belongs to an IP address space, which can be one of two different values:

  1. local: contains addresses that have meaning only within the current network. In other words, addresses whose target differs based on network position. This includes loopback addresses, which are only accessible on the local host (and thus differ for every device).

  2. public: contains all other addresses. In other words, addresses whose target is the same for all devices globally on the IP network.

For convenience, we additionally define the following terms:

  1. A local address is an IP address whose IP address space is local.

  2. A public address is an IP address whose IP address space is public.

An IP address space lhs is less public than an IP address space rhs if any of the following conditions holds true:

  1. lhs is local and rhs is public.

To determine the IP address space of an IP address address, run the following steps:

  1. If address belongs to the ::ffff:0:0/96 "IPv4-mapped Address" address block, then replace address with its embedded IPv4 address.

  2. For each row in the Non-public IP address blocks" table:

    1. If address belongs to row’s address block, return row’s address space.

  3. Return public.

Non-public IP address blocks
Address block Name Reference Address space
127.0.0.0/8 IPv4 Loopback [RFC1122] local
10.0.0.0/8 Private Use [RFC1918] local
100.64.0.0/10 Carrier-Grade NAT [RFC6598] local
172.16.0.0/12 Private Use [RFC1918] local
192.168.0.0/16 Private Use [RFC1918] local
198.18.0.0/15 Benchmarking [RFC2544] local
169.254.0.0/16 Link Local [RFC3927] private
::1/128 IPv6 Loopback [RFC4291] local
fc00::/7 Unique Local [RFC4193] local
fe80::/10 Link-Local Unicast [RFC4291] local
::ffff:0:0/96 IPv4-mapped [RFC4291] see mapped IPv4 address

User Agents MAY allow certain IP address blocks' address space to be overridden through administrator or user configuration. This could prove useful to protect e.g. IPv6 intranets where most IP addresses are considered public per the algorithm above, by instead configuring user agents to treat the intranet as local.

Note: Link-local IP addresses such as 169.254.0.0/16 are considered local, since such addresses can identify the same target for all devices on a network link. A previous version of this specification considered them to be local instead.

Note: The contents of each IP address space were at one point determined in accordance with the IANA Special-Purpose Address Registries ([IPV4-REGISTRY] and [IPV6-REGISTRY]) and the Globally Reachable bit defined therein. This turned out to be an inaccurate signal for our uses, as described in PNA’s spec issue #50](https://github.com/WICG/private-network-access/issues/50).

Note: [PRIVATE-NETWORK-ACCESS] used the address spaces public, private, and local. This specification simplifies the address spaces by combining [PRIVATE-NETWORK-ACCESS]’s private and local together into local.

2.2. Local Network Request

A request (request) is a local network request if request’s current url’s host maps to an IP address whose IP address space is less public than request’s policy container’s IP address space.

The classification of IP addresses into two broad address spaces is an imperfect and theoretically-unsound approach. It is a proxy used to determine whether two network endpoints should be allowed to communicate freely or not, in other words whether endpoint A is reachable from endpoint B without pivoting through the user agent on endpoint C.

This approach has some flaws:

Even so, this specification aims to offer a pragmatic solution to a security issue that broadly affects most users of the Web whose network configurations are not so complex.

The definition of local network requests could be expanded to cover all cross-origin requests for which the current url’s host maps to an IP address whose IP address space is not public. This would prevent a malicious server on the local network from attacking other servers, including servers on localhost. Currently, Chromium only implements Local Network Access restrictions for public to local requests, and does not enforce the permission for cross-origin local requests. This can be shipped as an incremental improvement later on. PNA Issue #39 We note that, because local names and addresses are not meaningful outside the bounds of the network, implementers may want to use a different permission prompt for the cross-origin local case than for the public to local case, and may want to scope these permission grants to the specific network or to the current browsing session only.

NOTE: Requests originating from the loopback address should not be considered local network requests, and should not be subject to local network access checks, since any software running on the user’s device is already in the most privileged vantage point on the user’s network.

NOTE: Some local network requests are more challenging to secure than others. See § 4.4 Rollout Difficulties for more details.

2.3. Local Network Request Permission Prompt

A local network access permission prompt is introduced to allow for users to approve of local network requests from public websites to local network servers.

When a local network request is detected, a prompt is shown to the user asking for permission to access the local network. If the user decides to grant the permission, then the fetch continues. If not, it fails.

The exact scope of the permission is implementation-defined. The permission may be as coarse-grained as allowing a specific origin to send local network requests to any endpoint on the local network, or may be more fine-grained to only allow specific origins to communicate with specific endpoints on the local network. A user agent may persist this decision to reduce permission fatigue.

2.4. Secure Context Restriction

The capability to make local network requests is a powerful feature and must only be allowed from secure contexts.

To be able to apply LNA checks to all cross-origin local requests (see Issue above), Chromium plans to exempt local servers that likely cannot currently get publicly trusted HTTPS certificates from this requirement (e.g., servers on .local and private IP literals). See § 4.4 Rollout Difficulties for more discussion, and also see https://github.com/WICG/private-network-access/issues/96.

2.5. Mixed Content

Many local network servers do not run HTTPS, as it has proven difficult (and sometimes even impossible) to migrate local network servers away from HTTP. This is problematic as the secure context restriction, combined with mixed content checks, would block many local network requests even if the user would give permission for the request to occur.

One solution to this problem is to bypass mixed content checks in situations where the request is known to be a local network request. This is known in a few situations:

There may be situations in which neither of the above situations is true, and yet the site wants to identify a request as being a local network request. This can be mitigated by adding a new parameter to the fetch() options bag:

fetch("http://router.local/ping", {
  targetAddressSpace: "local",
});

This instructs the browser to allow the fetch to bypass mixed-content checks even though the scheme is non-secure and potentially obtain a connection to the target server. The new fetch() API is backward-compatible.

Note that this feature cannot be abused to bypass mixed content in general. If the resolved remote IP address does not belong to the IP address space specified as the targetAddressSpace option value, then the request will fail. If it does belong, then the permission can be checked to allow or fail the request.

TODO: Decide if we want to keep the CSP directive treat-as-public-address around, see https://wicg.github.io/private-network-access/#csp. This directive would be obviated if we implemented https://github.com/wicg/private-network-access/issues/39.\]

3. Integrations

This section is non-normative.

This document proposes a number of modifications to other specifications in order to implement the mitigations sketched out in the examples above. These integrations are outlined here for clarity, but the external documents are the normative references.

3.1. Integration with Permissions

This document defines a powerful feature identified by the name "local-network-access". It overrides the following type:

permission descriptor type
The permission descriptor type of the "local-network-access" feature is defined by the following WebIDL interface that inherits from the default permission descriptor type:
dictionary LocalNetworkAccessPermissionDescriptor
    : PermissionDescriptor {
  DOMString id;
};

3.2. Integration with Fetch

This document proposes a few changes to Fetch, with the following implication: local network requests are only allowed if their client is a secure context and permission is granted by the user. If the request would have been blocked as mixed content, it can be allowed as long as the website states its intention to access the private network, and users give permission.

Note: This includes navigations. These can indeed be used to trigger CSRF attacks, albeit with less subtlety than with subresource requests.

Chromium only applies LNA restrictions to iframe navigations currently. It may be worth expanding this to include main-frame navigations (especially popup windows which can be controlled by their opener).

Note: [FETCH] does not yet integrate the details of DNS resolution into the Fetch algorithm, though it does define an obtain a connection algorithm which is enough for this specification. Local Network Access checks are applied to the newly-obtained connection. Given complexities such as Happy Eyeballs ([RFC6555], [RFC8305]), these checks might pass or fail non-deterministically for hosts with multiple IP addresses that straddle IP address space boundaries.

3.2.1. Fetching

What follows is a sketch of a potential solution:

  1. Connection objects are given a new IP address space property, initially null. This applies to WebSocket connections too.

  2. A new step is added to the obtain a connection algorithm immediately before appending connection to the user agent’s connection pool:

    1. Set connection’s IP address space to the result of running the determine the IP address space algorithm on the IP address of connection’s remote endpoint.

      The remote endpoint concept is not specified in [FETCH] yet, hence this is still handwaving to some extent.

  3. Request objects are given a new target IP address space property, initially null.

  4. Response objects are given a new IP address space property, whose value is an IP address space, initially null.

  5. Define a new Local Network Access check algorithm. Given a request request and a connection connection:

    1. If request’s origin is a potentially trustworthy origin and request’s current URL’s origin is same origin with request’s origin, then return null.

    2. If request’s policy container is null, then return null.

      NOTE: If request’s policy container is null, then LNA checks do not apply to request. Users of the fetch algorithm should take care to either set request’s client to an environment settings object with a non-null policy container and let fetch initialize request’s policy container accordingly, or to directly set request’s policy container to a non-null value.

    3. If request’s target IP address space is not null, then:

      1. Assert: request’s target IP address space is not public.

      2. If connection’s IP address space is not equal to then request’s target IP address space, then return a network error.

      3. Return null.

    4. If connection’s IP address space is less public than request’s policy container’s IP address space, then:

      1. Let error be a network error.

      2. If request’s client is not a secure context (including if it is null), then return error.

      3. Set error’s IP address space property to connection’s IP address space.

      4. TODO: Permission check is sketched out below, wording is still vague

        1. If the initiating origin has been granted the local network access permission, return null.

        2. If the initiating origin has been denied the local network access permission, return error.

        3. Otherwise, prompt the user:

          1. If the user grants permission, return null.

          2. If the user denies the permission, return error.

    5. Return null.

  6. The fetch algorithm is amended to add 2 new steps right after request’s policy container is set:

    1. If request’s target IP address space is null:

      1. If request’s URL’s host host is an IP address and the result of running the determine the IP address space algorithm on host is “local”, then set request’s target IP address space property to “local”.

      2. If request’s URL’s host’s public suffix is "local", then set request’s target IP address space property to "local".

        NOTE: We could also set the target IP address space to local if the request’s URL’s host is “localhost” or “127.0.0.1” (because of [let-localhost-be-localhost\]), but we do not need special handling for the loopback case as it is already considered to be potentially trustworthy and won’t trigger mixed content checks.

        NOTE: We also explicitly do not set the target address space property in the public case, because that breaks the next step here... (but maybe we could just skip that??)

        NOTE: We don’t set the target IP address space here if it was already non-null in order to prefer the explicit targetAddressSpace if set by the fetch() API.

    2. If request’s target IP address space is public, then return a network error.

      OPEN QUESTION: Do we need this? Under what conditions can this get set to "public"?

  7. The HTTP-network fetch algorithm is amended to add 3 new steps right after checking that the newly-obtained connection is not failure:

    1. Set response’s IP address space to connection’s IP address space.

    2. Let localNetworkAccessCheckResult be the result of running Local Network Access check for fetchParamsrequest and connection.

    3. If localNetworkAccessCheckResult is a network error, return localNetworkAccessCheckResult.

  8. Define a new algorithm called HTTP-no-service-worker fetch based on the existing steps in HTTP fetch that are run if response is still null after handling the fetch via service workers, and amend those slightly as follows:

    1. Immediately after running HTTP-network-or-cache fetch:

      1. If response is a network error and response’s IP address space is non-null, then:

        1. Set request’s target IP address space to response’s IP address space.

        2. Return the result of running HTTP-no-service-worker fetch given fetchParams.

      NOTE: Because request’s target IP address space is set to a non-null value when recursing, this recursion can go at most 1 level deep.

TODO: Figure out what we need to add for cache fetch. A sketch of Chromium’s behavior is included below in § 4.3 HTTP Cache.

NOTE: The requirement that local network requests be made from secure contexts means that any insecure request will be blocked as mixed content unless we can know ahead of time that the request should be considered a local network request. By setting the target IP address space property (see Step 6i and 6ii above), we only need to make a small change to Mixed Content -- see § 3.3 Integration with Mixed Content.

3.2.2. Fetch API

The Fetch API needs to be adjusted as well.

3.3. Integration with Mixed Content

The Should fetching request be blocked as mixed content? is amended to add the following condition to one of the allowed conditions:

  1. request’s origin is not a potentially trustworthy origin, and request’s target IP address space is local.

The "Upgrade request to an a priori authenticated URL as mixed content, if appropriate" algorithm is amended to add the following condition as an exception from upgrading in step 1:

  1. request’s target IP address space is local

3.4. Integration with WebSockets

WebSockets connections should be subject to the same local network access permission requirements.

[TODO: WebSockets “Obtain a WebSocket connection” is distinct, and a “WebSocket connection” is also distinct. So strictly by spec we need to duplicate some of our Fetch details but for WebSockets. See https://websockets.spec.whatwg.org/\#concept-websocket-connection-obtain\]

3.5. Integration with WebTransport

WebTransport connections should be subject to the same local network access permission requirements.

3.6. Integration with HTML

To support the checks in [FETCH], user agents must remember the source IP address space of contexts in which network requests are made. To this effect, the [HTML] specification is patched as follows:

  1. A new IP address space property is added to the policy container struct.

    1. It is initially public.

  2. An additional step is added to the clone a policy container algorithm:

    1. Set clone’s IP address space to policyContainer’s IP address space.

  3. An additional step is added to the create a policy container from a fetch response algorithm:

    1. Set result’s IP address space to response’s IP address space.

Assuming that example.com resolves to a public address (say, 123.123.123.123), then the Document created when navigating to https://example.com/document.html will have its policy container’s IP address space property set to public.

If this Document then embeds an about:srcdoc iframe, then the child frame’s Document will have its policy container’s IP address space property set to public.

If, on the other hand, example.com resolved to a local address (say, 127.0.0.1), then the Document created when navigating to https://example.com/document.html will have its policy container’s IP address space property set to local.

TODO: Also update the reference to Private Network Access in https://html.spec.whatwg.org/multipage/browsers.html#coep

3.7. Integration with Workers

This section is non-normative.

Given that WorkerGlobalScope already has a policy container field populated using the create a policy container from a fetch response algorithm, the avove integrations with Fetch and HTML apply just as well to worker contexts as to documents.

Assuming that example.com resolves to a public address (say, 123.123.123.123), then a WorkerGlobalScope created by fetching a script from https://example.com/worker.js will have its policy container’s IP address space property set to public.

Any fetch request initiated by this worker that obtains a connection to an IP address in the private or local address spaces would then be a private network request.

Chromium’s implementation currently applies the LNA permission for service worker-initiated fetches based on the worker’s script origin (since there may not be an active document around when the service worker is executing). It may be better for this to be based on the partitioned storage key of the worker, and it would also be good if permissions policy supported service workers

The Service Worker soft update algorithm unfortunately sets a request client of "null" when fetching an updated script. This causes all sorts of issues, and interferes with the local network access check algorithm laid out above. Indeed, there is no request client from which to copy the policy container during fetch. [[Issue \#83\]](https://github.com/wicg/private-network-access/issues/83)

4. Implementation considerations

4.1. File URLs

It isn’t entirely clear how file URLs fit into the public/local scheme outlined above. It would be nice to prevent folks from harming themselves by opening a malicious HTML file locally, on the one hand, but on the other, code running locally is somewhat outside of any coherent threat model.

For the moment, let’s err on the side of treating file URLs as local, as they seem to be just as much a part of the local system as anything else on a loopback address.

Re-evaluate this after implementation experience.

4.2. Proxies

In the current implementation of this specification in Chromium, proxies influence the address space of resources they proxy. Specifically, resources fetched via proxies are considered to have been fetched from the proxy’s IP address itself.

If a Document served by foo.example on a public address is fetched by the user agent via a proxy on a local address, then the Document’s policy container’s IP address space is set to local.

The Document will in turn be allowed to make requests to other local addresses accessible to the browser.

This can allow a website to learn that it was proxied by observing that it is allowed to make requests to local addresses, which is a privacy information leak. While this requires correctly guessing the URL of a resource on the local network, a single correct guess is sufficient.

This is expected to be relatively rare and not warrant more mitigations. After all, in the status quo all websites can make requests to all IP addresses with no restrictions whatsoever.

It would be interesting to explore a mechanism by which proxies could tell the browser "please treat this resource as public/local anyway", thereby passing on some information about the IP address behind the proxy.

4.3. HTTP Cache

The current implementation of this specification in Chromium interacts with the HTTP cache in two noteworthy ways, depending on which kind of resource is loaded from cache.

4.3.1. Main resources

A document constructed from a cached response remembers the IP address from which the response was initially loaded. The IP address space of the document is derived anew from the IP address.

In the common case, this entails that the document’s policy container’s IP address space is restored unmodified. However in the event that the user agent’s configuration has changed, the derived IP address space might be different.

The user agent navigates to http://foo.example/, loads the main resource from 1.2.3.4, caches it, then sets the resulting [document]'s [policy container]'s IP address space to public.

The user agent then restarts, and a new configuration is applied specifying that 1.2.3.4 should be classified as a local address instead.

The user agent navigates to http://foo.example/ once more and loads the main resource from the HTTP cache. The resulting [document]'s [policy container]'s IP address space is now set to local.

4.3.2. Subresources

Subresources loaded from the HTTP cache are subject to the Local Network Access check. This is not yet reflected in the algorithms above, since that check is only applied in HTTP-network fetch.

TODO: Specify and explain Chromium’s behavior here, or add an HTTP-network-or-cache fetch integration above. [[Issue \#75\]](https://github.com/wicg/private-network-access/issues/75) We include a sketch below.

As with main resources, a subresource constructed from a cached response remembers the IP address from which the response was initially loaded. The IP address space of the response is derived anew from the IP address.

In the common case, this entails that the response’s IP address space is restored unmodified. However, in the event that the user agent’s configuration has changed, the derived IP address space might be different.

When a subresource request is blocked by LNA checks (i.e., the permission was denied), there is no resource response cached. If the permission is later reset or granted, the subresource request will go to the network.

Previously allowed subresource load

The user agent navigates to https://foo.example, which is loaded from 1.2.3.4 (which has an IP address space of public). The document triggers a subresource request for http://bar.local/image.jpg, which when a connection is created has an IP address of 10.0.1.1 (which has an IP address space of local). This triggers a permission prompt, granting the Local Network Access permission to https://foo.example, and then the subresource is loaded and added to the user agent’s cache.

The user agent resets the permission for https://foo.example.

The user agent navigates to https://foo.example once more, which once again triggers a subresource request for http://bar.local/image.jpeg. This resource is in the user agent’s cache, with a cached response IP address of 10.0.1.1. This again triggers a permission prompt, which if granted will finish loading the resource from the user agent’s cache.

Previously blocked subresource load

The user agent navigates to https://foo.example, which is loaded from 1.2.3.4 (which has an IP address space of public). The document triggers a subresource request for http://bar.local/image.jpg, which when a connection is created has an IP address of 10.0.1.1 (which has an IP address space of local). This triggers a permission prompt, denying the Local Network Access permission to https://foo.example. The subresource request is blocked and no resource is added to the user agent’s cache.

The user agent resets the permission for https://foo.example.

The user agent navigates to https://foo.example once more, which once again triggers a subresource request for http://bar.local/image.jpeg. This resource is not in the user agent’s cache, so goes through HTTP network fetch and triggers the permission prompt.

See § 5.6 HTTP cache for a discussion of security implications.

4.4. Rollout Difficulties

Local Network Access essentially deprecates direct access to the local network in favor of more secure user-agent-mediated alternatives. Web deprecations are hard. Chromium previously encountered many stumbling blocks on the way to shipping parts of [PRIVATE-NETWORK-ACCESS] (the predecessor to this specification).

In particular, shipping restrictions on fetches from non-secure contexts in the local IP address space to services on localhost have proven particularly difficult, for a lower payoff. Indeed, exploiting such fetches requires attackers to already have a foothold in the private network, which substantially raises attack difficulty. As a result, Chromium is exempting these fetches from restrictions temporarily, choosing to focus on fetches from the public IP address space. See § 5.5 Local network attackers for a discussion of security implications.

5. Security and Privacy Considerations

TODO: add a reference to https://localmess.github.io/

5.1. User Mediation

The proposal in this document only ensures that the user consents to access from the public internet. This proposal does not allow for devices to explicitly approve for connections from the public internet.

An alternative model where devices had to explicitly approve for connections from the public internet was attempted in [PRIVATE-NETWORK-ACCESS], but ran into rollout difficulties.

TODO: link to some rollout difficulties of PNA

5.2. DNS rebinding

The mitigation described here operates upon the IP address which the user agent actually connects to when loading a particular resource. This check MUST be performed for each new connection made, as DNS rebinding attacks may otherwise trick the user agent into revealing information it shouldn’t.

DNS rebinding attacks also mean that local network access checks MUST apply to all public to local requests (that is, we cannot simplify the algorithm to be “any cross-origin request to a local endpoint”, as this would open a risk of a user navigation to a.example, then moving networks or having DNS change to make a.example point to a local address).

5.3. Scope of Mitigation

The proposal in this document merely mitigates attacks against local web services, it cannot fully solve them. For example, a router’s web-based administration interface must be designed and implemented to defend against CSRF on its own, and should not rely on a UA that behaves as specified in this document. The mitigation this document specifies is necessary given the reality of local web service implementation quality today, but vendors should not consider themselves absolved of responsibility, even if all UAs implement this mitigation.

5.4. Cross-network confusion

Most local networks cannot communicate with each other, yet they are all treated by this specification as belonging to the local IP address space. Going further, local addresses have meaning only on the local network where they are used. The same IP address might refer to entirely different devices in two different networks. A user granting permission for a.example to make local network requests could then move to a different network, and then a.example would continue to be able to make local network requests.

This opens the door to cross-network attacks:

None of these attacks are novel - they are just examples of the limitations of this specification.

Potential mitigations would require noticing network changes and clearing state specific to the previous network. Doing so in a fully general manner is likely to be impossible short of clearing all state. Maybe a practical compromise can be reached. PNA Issue #28

An alternative approach might be to scope the permission grant to an identifier that distinguishes different networks.

5.5. Local network attackers

Until local network access checks are applied to all cross-origin requests to the local address space, it is possible for a malicious server on the local network to (1) attack other servers on the local network, and (2) attack services running on localhost on a user’s machine. (1) is already possible without needing to abuse the user’s browser, but (2) remains a concern. For example, a captive portal might be able to redirect the user to a malicious page that tries to probe and attack vulnerable localhost services running on the user’s machine. (See also PNA Issue #39.)

We see public websites on the drive-by web as a more urgent security (and privacy) risk and see an incremental rollout of these protections as valuable progress, despite the lingering risk from local network attackers.

5.6. HTTP cache

5.6.1. Applying checks to subresources

Cached subresources are protected by this specification, as the HTTP cache remembers the source IP address which can be used in the Local Network Access check algorithm during HTTP-network-or-cache fetch.

Without this check, a malicious public website might be able to determine whether a user has visited particular private websites in the past.

Due to HTTP cache partitioning, a subresource can only be loaded from cache by malicious attackers who manage to replicate the network partition key of the cache entry. One way an attacker could achieve this is by manipulating DNS (see also § 5.2 DNS rebinding in order to impersonate the top-level site that initially embedded the cached resource.

The user agent navigates to http://router.example, which is served from 192.168.1.1. The website embeds a logo from http://router.example/$BRAND-logo.png, which is cached.

A malicious attacker then re-binds router.example to an attacker-controlled public IP address, and somehow tricks the user into visiting http://router.example again. The malicious website attempts to embed the logo, and monitors whether the load is successful. If so, the attacker has determined the brand of the user’s router.

5.6.2. HTTP cache poisoning

While this specification aims to protect local network servers from receiving requests from public websites, DNS rebinding can be used to carry out a similar attack through cache poisoning of unauthenticated resources.

Attackers masquerading as http://router.com can cache a malicious script at http://router.com/totally-legit.js. Later on, when the user navigates to http://router.com/, the page might request the poisoned script and execute attacker code in a less public IP address space.

This attack is partially mitigated by cache partitioning, which makes it so that the attacker must navigate a top-level browsing context to http://router.com/ before caching resources, which lacks subtlety. It is also not specific to Local Network Access, rather being a symptom of plaintext HTTP’s lack of authentication and integrity protection.

6. Acknowledgements

Many thanks for valuable feedback and advice from Titouan Rigoudy, Jonathan Hao, and Yifan Luo who worked on the original Private Network Access proposals and specification, and generously discussed their work and helped brainstorm paths forward.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Tests

Tests relating to the content of this specification may be documented in “Tests” blocks like this one. Any such block is non-normative.


Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MIXED-CONTENT]
Emily Stark; Mike West; Carlos IbarraLopez. Mixed Content. URL: https://w3c.github.io/webappsec-mixed-content/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PRIVATE-NETWORK-ACCESS]
Private Network Access. Draft Community Group Report. URL: https://wicg.github.io/private-network-access/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[AVASTIUM]
Avast: A web-accessible RPC endpoint can launch 'SafeZone' (also called Avastium), a Chromium fork with critical security checks removed.. URL: https://bugs.chromium.org/p/project-zero/issues/detail?id=679
[CSRF-EXPLOIT-KIT]
Kafeine. An Exploit Kit dedicated to CSRF Pharming. URL: http://malware.dontneedcoffee.com/2015/05/an-exploit-kit-dedicated-to-csrf.html
[DRIVE-BY-PHARMING]
Sid Stamm; Zulfikar Ramzan; Markus Jakobsson. Drive-By Pharming. URL: https://link.springer.com/chapter/10.1007/978-3-540-77048-0_38
[IPV4-REGISTRY]
IANA IPv4 Special-Purpose Address Registry. URL: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
[IPV6-REGISTRY]
IANA IPv6 Special-Purpose Address Registry. URL: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
[RFC1122]
R. Braden, Ed.. Requirements for Internet Hosts - Communication Layers. October 1989. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc1122
[RFC1918]
Y. Rekhter; et al. Address Allocation for Private Internets. February 1996. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc1918
[RFC2544]
S. Bradner; J. McQuaid. Benchmarking Methodology for Network Interconnect Devices. March 1999. Informational. URL: https://www.rfc-editor.org/rfc/rfc2544
[RFC3927]
S. Cheshire; B. Aboba; E. Guttman. Dynamic Configuration of IPv4 Link-Local Addresses. May 2005. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc3927
[RFC4193]
R. Hinden; B. Haberman. Unique Local IPv6 Unicast Addresses. October 2005. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc4193
[RFC4291]
R. Hinden; S. Deering. IP Version 6 Addressing Architecture. February 2006. Draft Standard. URL: https://www.rfc-editor.org/rfc/rfc4291
[RFC6555]
D. Wing; A. Yourtchenko. Happy Eyeballs: Success with Dual-Stack Hosts. April 2012. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6555
[RFC6598]
J. Weil; et al. IANA-Reserved IPv4 Prefix for Shared Address Space. April 2012. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc6598
[RFC8305]
D. Schinazi; T. Pauly. Happy Eyeballs Version 2: Better Connectivity Using Concurrency. December 2017. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8305
[SOHO-PHARMING]
Team Cymru. SOHO Pharming. URL: https://331.cybersec.fun/TeamCymruSOHOPharming.pdf
[TREND-MICRO]
TrendMicro node.js HTTP server listening on localhost can execute commands. URL: https://bugs.chromium.org/p/project-zero/issues/detail?id=693

IDL Index

enum IPAddressSpace { "public", "local" };

dictionary LocalNetworkAccessPermissionDescriptor
    : PermissionDescriptor {
  DOMString id;
};

partial dictionary RequestInit {
  IPAddressSpace targetAddressSpace;
};

partial interface Request {
  readonly attribute IPAddressSpace targetAddressSpace;
};

Issues Index

The definition of local network requests could be expanded to cover all cross-origin requests for which the current url’s host maps to an IP address whose IP address space is not public. This would prevent a malicious server on the local network from attacking other servers, including servers on localhost. Currently, Chromium only implements Local Network Access restrictions for public to local requests, and does not enforce the permission for cross-origin local requests. This can be shipped as an incremental improvement later on. PNA Issue #39 We note that, because local names and addresses are not meaningful outside the bounds of the network, implementers may want to use a different permission prompt for the cross-origin local case than for the public to local case, and may want to scope these permission grants to the specific network or to the current browsing session only.
To be able to apply LNA checks to all cross-origin local requests (see Issue above), Chromium plans to exempt local servers that likely cannot currently get publicly trusted HTTPS certificates from this requirement (e.g., servers on .local and private IP literals). See § 4.4 Rollout Difficulties for more discussion, and also see https://github.com/WICG/private-network-access/issues/96.
Chromium only applies LNA restrictions to iframe navigations currently. It may be worth expanding this to include main-frame navigations (especially popup windows which can be controlled by their opener).
The remote endpoint concept is not specified in [FETCH] yet, hence this is still handwaving to some extent.
Chromium’s implementation currently applies the LNA permission for service worker-initiated fetches based on the worker’s script origin (since there may not be an active document around when the service worker is executing). It may be better for this to be based on the partitioned storage key of the worker, and it would also be good if permissions policy supported service workers
The Service Worker soft update algorithm unfortunately sets a request client of "null" when fetching an updated script. This causes all sorts of issues, and interferes with the local network access check algorithm laid out above. Indeed, there is no request client from which to copy the policy container during fetch. [[Issue \#83\]](https://github.com/wicg/private-network-access/issues/83)
Re-evaluate this after implementation experience.
Potential mitigations would require noticing network changes and clearing state specific to the previous network. Doing so in a fully general manner is likely to be impossible short of clearing all state. Maybe a practical compromise can be reached. PNA Issue #28