Private Network Access

Draft Community Group Report,

This version:
https://wicg.github.io/private-network-access/
Previous Versions:
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

This document specifies modifications to Fetch which are intended to mitigate the risks associated with unintentional exposure of devices and servers on a client’s internal network to the web at large.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is not normative.

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

Here, we propose a mitigation against these kinds of attacks that would require internal devices to explicitly opt-in to requests from the public internet.

1.1. Goals

The overarching goal is to prevent the user agent from inadvertantly 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:

1.2. Examples

1.2.1. Secure by Default

MegaCorp Inc’s routers have a fairly serious CSRF vulnerability which allows their DNS settings to be altered by navigating to http://admin:admin@router.local/set_dns and passing in various GET parameters. Oh noes!

Happily, MegaCorp Inc’s routers don’t have any interest in requests from the public internet, and didn’t take any special effort to enable them. This greatly mitigates the scope of the vulnerability, as malicious requests will generate a CORS-preflight request, which the router ignores. Let’s take a closer look:

Given https://csrf.attack/ that contains the following HTML:

<iframe href="https://admin:admin@router.local/set_dns?server1=123.123.123.123">
</iframe>

router.local will be resolved to the router’s address via the magic of multicast DNS [RFC6762], and the user agent will note it as private. Since csrf.attack resolved to a public address, the request will trigger a CORS-preflight request:

OPTIONS /set_dns?... HTTP/1.1
Host: router.local
Access-Control-Request-Method: GET
Access-Control-Request-Private-Network: true
...
Origin: https://csrf.attack

The router will receive this OPTIONS request, and has a number of possible safe responses:

  • If it doesn’t understand OPTIONS at all, it can return a 50X error. This will cause the preflight to fail, and the actual GET will never be issued.

  • If it does understand OPTIONS, it can neglect to include an Access-Control-Allow-Private-Network header in its response. This will cause the preflight to fail, and the actual GET will never be issued.

  • It can crash. Crashing is fairly safe, if inelegant.

1.2.2. Opting-In

Some of MegaCorp Inc’s devices actually need to talk to the public internet for various reasons. They can explicitly opt-in to receiving requests from the internet by sending proper CORS headers in response to a CORS-preflight request.

When a website on the public internet makes a request to the device, the user agent determines that the requestor is public, and the router is private. This means that requests will trigger a CORS-preflight request, just as above.

The device can explicitly grant access by sending the right headers in its response to the preflight request. For the above request, that might look like:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://public.example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Private-Network: true
Vary: Origin
Content-Length: 0
...
MegaCorp Inc. runs an internal link shortening service at https://go/, and its employees often email such links to each other. The email server is hosted at a public address in order to ensure that employees can work even when they’re not at the office. How considerate!

Clicking http://go/* links from https://mail.mega.corp/ will trigger a CORS-preflight request, as it is a request from a public address to a private address:

OPTIONS /short-links-are-short-after-shortening HTTP/1.1
Host: go
Access-Control-Request-Method: GET
Access-Control-Request-Private-Network: true
...
Origin: https://mail.mega.corp

In order to ensure that employees can continue to navigate such links as expected, MegaCorp chooses to allow private network requests:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://mail.mega.corp
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Private-Network: true
Vary: Origin
Content-Length: 0
...

MegaCorp’s leak-prevention department is worried, though, that this access will allow external folks to read the location of any redirect that the shortener would return. They’re more or less resigned to the fact that https://go/shortlink will leak, but would be sad indeed if the target (https://sekrits/super-sekrit-project-with-super-sekrit-partner) leaked as well.

MegaCorp’s shortlink engineers are careful to avoid this potential failure by returning CORS headers only for the preflight. The "real" navigation doesn’t require CORS headers, and they don’t actually want to support cross-origin requests as being CORS-same-origin:

// Request:
GET /short-links-are-short-after-shortening HTTP/1.1
Host: go
Access-Control-Request-Private-Network: true
...
Origin: https://mail.mega.corp

// Response:
HTTP/1.1 301 Moved Permanently
...
Location: https://sekrits/super-sekrit-project-with-super-sekrit-partner

The navigation will proceed normally, but mail.mega.corp won’t be considered CORS-same-origin with the response.

2. Framework

2.1. IP Address Space

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

  1. local: contains the local host only. In other words, addresses whose target differs for every device.

  2. private: contains addresses that have meaning only within the current network. In other words, addresses whose target differs based on network position.

  3. 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 private address is an IP address whose IP address space is private.

  3. 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 either private or public.

  2. lhs is private 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] private
172.16.0.0/12 Private Use [RFC1918] private
192.168.0.0/16 Private Use [RFC1918] private
169.254.0.0/16 Link Local [RFC3927] private
::1/128 IPv6 Loopback [RFC4291] local
fc00::/7 Unique Local [RFC4193] private
fe80::/10 Link-Local Unicast [RFC4291] private
::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 private.

Note: Link-local IP addresses such as 169.254.0.0/16 are considered private, 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 spec issue #50.

Remove the special case for IPv4-mapped IPv6 addresses once access to these addresses is blocked entirely. <https://github.com/wicg/private-network-access/issues/36>

2.2. Private Network Request

A request (request) is a private network request if request’s current url's host maps to an IP address whose IP address space is less public than request’s [request/policy container=]'s IP address space.

The classification of IP addresses into three 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 private 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 private network from attacking other servers. The effort require to ship such a change is not deemed worth the payoff for now. This can be shipped as an incremental improvement later on. <https://github.com/wicg/private-network-access/issues/39>

2.3. Additional CORS Headers

The Access-Control-Request-Private-Network indicates that the request is a private network request.

The Access-Control-Allow-Private-Network indicates that a resource can be safely shared with external networks.

2.4. The treat-as-public-address Content Security Policy Directive

The treat-as-public-address directive instructs the user agent to treat a document as though it was served from a public address, even if it was actually served from a private address or a local address. That is, it is a mechanism by which non-public documents may drop the privilege to contact other non-public documents without a preflight.

The directive’s syntax is described by the following ABNF grammar:

directive-name  = "treat-as-public-address"
directive-value = ""

This directive has no reporting requirements; it will be ignored entirely when delivered in a Content-Security-Policy-Report-Only header, or within a meta element.

This directive’s initialization algorithm is as follows. Given an environment settings object (context), a Response (response), and a policy (policy):

  1. Set context’s policy container's IP address space to public if policy’s disposition is "enforce".

2.5. Feature Detection

A previous version of this specification proposed adding an addressSpace enum property to Document and WorkerGlobalScope, but it was removed due to fingerprinting concerns (see issue #21).

Documents should not behave differently or not based on whether the UA implements this specification or not - all documents should assume it does.

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 Fetch

This document proposes a few changes to Fetch, with the following implication: private network requests are only allowed if their client is a secure context and a CORS-preflight request to the target origin is successful.

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

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. Private 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.1.1. Secure context restriction

UAs must not allow non-secure public contexts to request resources from private addresses, even if the private server would opt-in to such a request via a preflight. Making requests to private resources presents risks which are mitigated by ensuring the integrity of the client which initiates the request. In particular, network attackers should not be able to trivially exploit an endpoint’s consent to a non-secure origin.

To that end:

  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. <https://github.com/wicg/private-network-access/issues/33>

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

  4. Define a new Private Network Access check algorithm. Given a request request and a connection connection:

    1. If request’s client is a non-secure context:

      1. Let clientAddressSpace be request’s policy container's IP address space.

      2. If connection’s IP address space is less public than clientAddressSpace, then return a network error.

    2. Return null.

  5. 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 privateNetworkAccessCheckResult be the result of running Private Network Access check for fetchParamsrequest and connection.

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

3.1.2. CORS preflight

The HTTP fetch algorithm should be adjusted to ensure that a preflight is triggered for all private network requests initiated from secure contexts.

The main issue here is again that the response’s IP address space is not known until a connection is obtained in HTTP-network fetch, which is layered under CORS-preflight fetch. What follows is a sketch of a potential solution:

  1. Add a new target IP address space property to the request struct, initially null.

  2. Amend the Private Network Access check algorithm to handle this new property and propagate the IP address space of the newly-obtained connection to the caller of the HTTP-network fetch algorithm when the client is a secure context:

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

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

      2. Return null.

    2. Let clientAddressSpace be request’s policy container's IP address space.

    3. If response’s IP address space is less public than clientAddressSpace, then:

      1. Let error be a network error.

      2. If request’s client is a secure context, then set error’s IP address space property to response’s IP address space.

      3. Return error.

    4. Return null.

  3. 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. At the very start:

      1. If request’s target IP address space is not null, then set makeCORSPreflight to true.

    2. Immediately after running CORS-preflight fetch:

      1. If preflightResponse is a network error:

        1. If preflightResponse’s IP address space is null, return preflightResponse.

        2. Set request’s target IP address space to preflightResponse’s IP address space.

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

    3. 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 preflightResponse’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.

  4. The CORS-preflight fetch algorithm is adjusted to handle the new headers:

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

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

        1. Set "Access-Control-Request-Private-Network" to "true" in preflight’s header list.

    2. Immediately after checking CORS-unsafe request-header names in response against request’s header list:

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

        1. Let allow be the result of extracting header list values given "Access-Control-Allow-Private-Network" and response’s header list.

        2. If allow is not "true", return a network error.

  5. Finally, to mitigate the impact of DNS rebinding attacks (see § 5.3 DNS Rebinding), the CORS-preflight cache is adjusted to take IP address space information into account:

    1. A new IP address space property (null or an IP address space) is added to each cache entry.

    2. This new property is initialized by the create a new cache entry algorithm from request’s target IP address space.

    3. This new property is checked by the cache entry match algorithm:

      1. entry’s IP address space is equal to request’s target IP address space.

3.2. Integration with WebSockets

Preflight requests should probably be sent ahead of WebSocket handshakes, given that WebSocket handshakes have roughly the same capabilities as <img> tags. This might require no additional work to specify given that the establish a WebSocket connection depends on the Fetch algorithm. <https://github.com/wicg/private-network-access/issues/14>

A previous version of this specification proposed simply adding the new headers (see § 2.3 Additional CORS Headers) to the WebSocket handshake. This would not be sufficient to fully guard against CSRF attacks, however.

3.3. 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.

4. Implementation Considerations

4.1. Where do file URLs fit?

It isn’t entirely clear how file URLs fit into the public/private 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 sane 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.

Reevaluate 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 private address, then the Document's policy container's IP address space is set to private.

The Document will in turn be allowed to make requests to other private 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 private addresses, which is a privacy information leak. While this requires correctly guessing the URL of a resource on the private 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/private anyway", thereby passing on some information about the IP address behing the proxy. This might take the form of the CSP directive discussed above, with some minor modifications.

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 whence 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 private 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 private.

4.3.2. Subresources

Subresources loaded from the HTTP cache are not subject to the Private Network Access check, since that check is only applied in HTTP-network fetch.

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

5. Security and Privacy Considerations

5.1. User Mediation

The proposal in this document only ensures that the device consents to access from the public internet. Users agents MAY ensure that the user consents to such access as well, as it might be in their interests to deny such access, even though the device itself would allow it.

This mediation could be done via an explicit permission grant, via some sort of pairing ceremony a la PAKE, or any other clever interface which the user agent might devise.

5.2. Mixed Content

The CORS restrictions added by the proposal in this document do not obviate mixed content checks [MIXED-CONTENT-2]. Device consent obtained through a CORS preflight request is necessary but not sufficient.

Note: [MIXED-CONTENT-2] does not prevent secure contexts from fetching resources from origins whose host is localhost or an IP address in the 127.0.0.0/8 or ::1/128 blocks. See also the definition of potentially trustworthy origins.

Developers who wish to fetch private or local resources (from hosts other than the above exceptions) from public pages MUST ensure that the connection is secure. This might involve a solution along the lines of [PLEX], where Web PKI certificates are issued to user-specific domain names that then resolve to private IP addresses which only make sense on the user’s private network.

Some consumer routers implement overly-aggressive protections against DNS rebinding attacks by simply blocking DNS responses that resolve to non-public IP addresses. This presents a stumbling block for solutions like [PLEX]. Workarounds are discussed in the linked issue. <https://github.com/wicg/private-network-access/issues/23>

This problem space has been explored a few times already and seems worth revisiting at some point. One could imagine a pairing ceremony such as the one hinted at above, or one of the ideas floated in [SECURE-LOCAL-COMMUNICATION].

5.3. 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.

The modifications to the CORS-preflight cache are intended to mitigate this attack vector.

5.4. Scope of Mitigation

The proposal in this document merely mitigates attacks against private 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 private web service implementation quality today, but vendors should not consider themselves absolved of responsibility, even if all UAs implement this mitigation.

5.5. Cross-network confusion

Most private networks cannot communicate with each other, yet they are all treated by this specification as belonging to the private IP address space. Going further, private addresses have meaning only on the private network where they are used. The same IP address might refer to entirely different devices in two different networks.

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. <https://github.com/wicg/private-network-access/issues/28>

5.6. HTTP cache

Cached subresources are not currently protected by this specification, even though the HTTP cache remembers the source IP address which could be used in the Private Network Access check algorithm during HTTP-network-or-cache fetch.

While it may be a good idea to fix this apparent discrepancy, it is not directly relevant to the main goal of this specification: preventing CSRF attacks.

At most, a malicious public website might be able to determine whether a user has visited particular private websites in the past. This attack on the user’s privacy is no worse than the status quo.

In addition, 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.3 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.

6. IANA Considerations

The Content Security Policy Directive registry should be updated with the following directives and references [RFC7762]:

treat-as-public-address

This document (see § 2.4 The treat-as-public-address Content Security Policy Directive)

7. Acknowledgements

Conversations with Ryan Sleevi, Chris Palmer, and Justin Schuh helped flesh out the contours of this proposal. Hopefully they won’t hate it too much. Mathias Karlsson has the dubious honor of being the straw that broke the camel’s back, and Brian Smith’s contributions to the resulting thread were useful, as always.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSP3]
Mike West. Content Security Policy Level 3. 12 May 2021. WD. URL: https://www.w3.org/TR/CSP3/
[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/
[RFC7762]
M. West. Initial Assignment for the Content Security Policy Directives Registry. January 2016. Informational. URL: https://datatracker.ietf.org/doc/html/rfc7762
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.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://code.google.com/p/google-security-research/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
[MIXED-CONTENT-2]
Emily Stark; Mike West; Carlos Ibarra Lopez. Mixed Content Level 2. 14 October 2020. W3C First Public Working Draft. URL: https://w3c.github.io/webappsec-mixed-content/level2.html
[PLEX]
Filippo Valsorda. How Plex is doing HTTPS for all its users. URL: https://blog.filippo.io/how-plex-is-doing-https-for-all-its-users/
[RFC1122]
R. Braden, Ed.. Requirements for Internet Hosts - Communication Layers. October 1989. Internet Standard. URL: https://datatracker.ietf.org/doc/html/rfc1122
[RFC1918]
Y. Rekhter; et al. Address Allocation for Private Internets. February 1996. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc1918
[RFC3927]
S. Cheshire; B. Aboba; E. Guttman. Dynamic Configuration of IPv4 Link-Local Addresses. May 2005. Proposed Standard. URL: https://datatracker.ietf.org/doc/html/rfc3927
[RFC4193]
R. Hinden; B. Haberman. Unique Local IPv6 Unicast Addresses. October 2005. Proposed Standard. URL: https://datatracker.ietf.org/doc/html/rfc4193
[RFC4291]
R. Hinden; S. Deering. IP Version 6 Addressing Architecture. February 2006. Draft Standard. URL: https://datatracker.ietf.org/doc/html/rfc4291
[RFC6555]
D. Wing; A. Yourtchenko. Happy Eyeballs: Success with Dual-Stack Hosts. April 2012. Proposed Standard. URL: https://datatracker.ietf.org/doc/html/rfc6555
[RFC6762]
S. Cheshire; M. Krochmal. Multicast DNS. February 2013. Proposed Standard. URL: https://datatracker.ietf.org/doc/html/rfc6762
[RFC8305]
D. Schinazi; T. Pauly. Happy Eyeballs Version 2: Better Connectivity Using Concurrency. December 2017. Proposed Standard. URL: https://datatracker.ietf.org/doc/html/rfc8305
[SECURE-CONTEXTS]
Mike West. Secure Contexts. 15 September 2016. CR. URL: https://www.w3.org/TR/secure-contexts/
[SECURE-LOCAL-COMMUNICATION]
Minutes from 'Secure communication with local network devices': TPAC, 2015. URL: http://www.w3.org/2015/10/28-local-minutes.html
[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://code.google.com/p/google-security-research/issues/detail?id=693

Issues Index

Remove the special case for IPv4-mapped IPv6 addresses once access to these addresses is blocked entirely. <https://github.com/wicg/private-network-access/issues/36>
The definition of private 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 private network from attacking other servers. The effort require to ship such a change is not deemed worth the payoff for now. This can be shipped as an incremental improvement later on. <https://github.com/wicg/private-network-access/issues/39>
The remote endpoint concept is not specified in [FETCH] yet, hence this is still handwaving to some extent. <https://github.com/wicg/private-network-access/issues/33>
Preflight requests should probably be sent ahead of WebSocket handshakes, given that WebSocket handshakes have roughly the same capabilities as <img> tags. This might require no additional work to specify given that the establish a WebSocket connection depends on the Fetch algorithm. <https://github.com/wicg/private-network-access/issues/14>
Reevaluate this after implementation experience.
Some consumer routers implement overly-aggressive protections against DNS rebinding attacks by simply blocking DNS responses that resolve to non-public IP addresses. This presents a stumbling block for solutions like [PLEX]. Workarounds are discussed in the linked issue. <https://github.com/wicg/private-network-access/issues/23>
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. <https://github.com/wicg/private-network-access/issues/28>