Skip to content

Latest commit

 

History

History
212 lines (161 loc) · 9.66 KB

explainer.md

File metadata and controls

212 lines (161 loc) · 9.66 KB

Explainer: Private Network Access

Quick links

Introduction

Private Network Access is a web specification which aims to protect websites accessed over the private network (either on localhost or a private IP address) from malicious requests from websites located outside the private network.

Say you visit evil.com, we want to prevent it from using your browser as a springboard to hack your printer. Perhaps surprisingly, evil.com can easily accomplish that in most browsers today (given a web-accessible printer exploit).

This specification only affects requests from a public IP address to a private IP address or localhost, and requests from a private IP address to localhost. This may change to cover all cross-origin requests to the private network in the future, see issue #39.

This specification used to be named "CORS-RFC1918" , after CORS, which provides a mechanism for securing websites against cross-origin requests, and RFC 1918, which describes IPv4 address ranges reserved for private networks.

Goals

The overarching goal is to prevent malicious websites from pivoting through the user agent's network position to attack devices and services which reasonably assumed they were unreachable from the Internet at large, by virtue of residing on the user’s local intranet or the user's machine.

For example, we wish to mitigate attacks on:

  • Users' routers, as outlined in SOHO Pharming. Note that status quo CORS protections don’t protect against the kinds of attacks discussed here as they rely only on CORS-safelisted methods and CORS-safelisted request-headers. No preflight is triggered, and the attacker doesn’t actually care about reading the response, as the request itself is the CSRF attack.
  • Software running a web interface on a user’s loopback address. For better or worse, this is becoming a common deployment mechanism for all manner of applications, and often assumes protections that simply don’t exist (see recent examples).

Non-goals

Provide a secure mechanism for initiating HTTPS connections to services running on the local network or the user’s machine. This piece is missing to allow secure public websites to embed non-public resources without running into mixed content violations, with the exception of http://localhost which is embeddable. While a useful goal, and maybe even a necessary one in order to deploy Private Network Access more widely, it is out of scope of this specification.

Proposed design

Address spaces

We extend the RFC 1918 concept of private IP addresses to build a model of network privacy. In this model, there are 3 main layers to an IP network from the point of view of a node, which we organize from most to least private:

  • Localhost - accessible only to the node itself, by default
  • Private IP addresses - accessible only to the members of the private network
  • Public IP addresses - accessible to anyone

We call these layers address spaces: local, private, and public.

The mapping from IP address to address space is defined in the specification, and may be overridden by user agents through user or administrator configuration.

It might also be useful to consider web origins when determining address spaces. For example, the .local. top-level DNS domain (see RFC 6762) might be always be considered private. See this discussion.

Proxies

Proxies influence the address space of resources they proxy. If foo.example, served on a public IP address, is accessed by a browser via a proxy on a private IP address (e.g. 192.168.1.123), then the resource will be considered to have been fetched from a private IP address. The resource will in turn be allowed to make requests to other private IP addresses accessible to the browser. This can allow foo.example 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 below, with some minor modifications.

Private network requests

The address space concept and accompanying model of IP address privacy lets us define the class of requests we wish to secure.

We define a private network request as a request crossing an address space boundary to a more-private address space.

Concretely, there are 3 kinds of private network requests:

  1. public -> private
  2. public -> local
  3. private -> local

Note that private -> private is not a private network request, as well as local -> anything.

Integration with Fetch

Private network requests are handled differently than others, like so:

  • If the client is not in a secure context, the request is blocked.
  • Otherwise, the original request is preceded by a CORS pre-flight request.
    • CORS safelisting checks are skipped if the CORS preflight is only sent for PNA. (i.e. it would not have been sent without PNA)
    • The pre-flight request carries an additional Access-Control-Request-Private-Network: true header.
    • The response must carry an additional Access-Control-Allow-Private-Network: true header.

The Fetch spec does not yet integrate the details of DNS resolution, only defining an obtain a connection algorithm, thus 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.

Integration with HTML

Documents and WorkerGlobalScopes store an additional address space value. This is initialized from the IP address the document or worker was sourced from.

A new directive is introduced to CSP: treat-as-public-address. If a document or worker's CSP list includes this directive, then its address space is set to public unconditionally.

A previous version of this specification introduced a new addressSpace attribute to Document and WorkerGlobalScope to allow for introspection from Javascript. This was removed over security and privacy concerns, see issue #21.

Integration with WebSockets

Preflight requests should be sent ahead of WebSocket handshakes, given that said handshakes have roughly the same capabilities for CSRF as <img> tags. This might require no additional work to specify given that the establish a WebSocket connection algorithm depends on the Fetch algorithm. See also issue #14.

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

Security and Privacy considerations

See also ./security_privacy_self_review.md.

HTTP cache

Documents restored from the HTTP cache are placed in the same IP address space as the original document loaded from the network. This avoids trivially bypassing Private Network Access checks by round-tripping a document through the HTTP cache.

Note that if the browser's configuration changes in between loads and the new configuration states that the original IP address should be placed in a different IP address space, then the restored document will not belong to the same IP address space as the original document.

Subresources loaded from the HTTP cache are subject to the same Private Network Access checks as if they were loaded from the origin IP address. This does not immediately prevent CSRF attacks, yet is included for coherence.

Alternatives considered

Cover all cross-origin requests targeting the local network

Define private network request instead as: any request targeting an IP address in the local or private address spaces, regardless of the requestor’s address space.

This definition is strictly broader than the one we are currently working with. It seems likely to cause more widespread breakage in the web ecosystem. We would like to first launch the current version and consider this instead as an interesting avenue for future work.