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 internal 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].
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 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:
-
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 [AVASTIUM] and [TREND-MICRO] for recent examples).
1.2. Examples
1.2.1. Secure by Default
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 local. 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-Local-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 a50X
error. This will cause the preflight to fail, and the actualGET
will never be issued. -
If it does understand
OPTIONS
, it can neglect to include anAccess-Control-Allow-Local-Network
header in its response. This will cause the preflight to fail, and the actualGET
will never be issued. -
It can crash. Crashing is fairly safe, if inelegant.
1.2.2. Opting-In
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 local. 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-Local-Network: true Content-Length: 0 ...
1.2.3. Navigation
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 https://go/*
links from https://mail.mega.corp/
will trigger a CORS-preflight request, as it is a request from a public
address to a local address:
OPTIONS /short-links-are-short-after-shortening HTTP/1.1 Host: go Access-Control-Request-Method: GET Access-Control-Request-Local-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 local 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-Local-Network: true 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 ... // 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:
-
loopback: contains the local host only. In other words, addresses whose target differs for every device.
-
local: contains addresses that have meaning only within the current network. In other words, addresses whose target differs based on network position.
-
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:
-
A loopback address is an IP address whose IP address space is loopback.
-
A local address is an IP address whose IP address space is local.
-
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:
To determine the IP address space of an IP address address, run the following steps:
-
If address belongs to the
::ffff:0:0/96
"IPv4-mapped Address" address block, then replace address with its embedded IPv4 address. -
For each row in the Non-public IP address blocks" table:
-
If address belongs to row’s address block, return row’s address space.
-
-
Return public.
Address block | Name | Reference | Address space |
---|---|---|---|
127.0.0.0/8
| IPv4 Loopback | [RFC1122] | loopback |
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 |
169.254.0.0/16
| Link Local | [RFC3927] | local |
::1/128
| IPv6 Loopback | [RFC4291] | loopback |
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 loopback 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. [Issue #36]
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 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:
-
false positives: an intranet server with a public address might be able to directy issue requests to another server on the same intranet with a local address.
-
false negatives: a client connected to two different local networks, say a home network and a VPN, might allow a website served from the VPN to access devices on the home network. See also the issue below.
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. 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. [Issue #39]
NOTE: Some local network requests are more challenging to secure than others. See § 4.4 Rollout difficulties for more details.
2.3. Additional CORS Headers
The Access-Control-Request-Local-Network
indicates that the request is a local network request.
The Access-Control-Allow-Local-Network
indicates that a resource can be safely shared with external networks.
Note: Previous versions of this spec spelled the headers as Access-Control-Request-Private-Network
and Access-Control-Allow-Private-Network
.
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 local address or a loopback 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):
-
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: local 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. 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.1.1. Secure context restriction
UAs must not allow non-secure public contexts to request resources from local addresses, even if the local server would opt-in to such a request via a preflight. Making requests to local 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:
-
Connection objects are given a new IP address space property, initially null. This applies to WebSocket connections too.
-
A new step is added to the obtain a connection algorithm immediately before appending connection to the user agent’s connection pool:
-
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. [Issue #33]
-
-
Response objects are given a new IP address space property, whose value is an IP address space, initially null.
-
Define a new Local Network Access check algorithm. Given a request request and a connection connection:
-
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.
-
If request’s client is a non-secure context:
-
Let clientAddressSpace be request’s policy container's IP address space.
-
If connection’s IP address space is less public than clientAddressSpace, then return a network error.
-
-
Return null.
-
-
The HTTP-network fetch algorithm is amended to add 3 new steps right after checking that the newly-obtained connection is not failure:
-
Set response’s IP address space to connection’s IP address space.
-
Let localNetworkAccessCheckResult be the result of running Local Network Access check for fetchParams’ request and connection.
-
If localNetworkAccessCheckResult is a network error, return localNetworkAccessCheckResult.
-
3.1.2. CORS preflight
The HTTP fetch algorithm should be adjusted to ensure that a preflight is triggered for all local 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:
-
Add a new target IP address space property to the request struct, initially null.
-
Amend the Local 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:
-
If request’s target IP address space is not null, then:
-
If connection’s IP address space is not equal to then request’s target IP address space, then return a network error.
-
Return null.
-
-
Let clientAddressSpace be request’s policy container's IP address space.
-
If response’s IP address space is less public than clientAddressSpace, then:
-
Let error be a network error.
-
If request’s client is a secure context, then set error’s IP address space property to response’s IP address space.
-
Return error.
-
-
Return null.
-
-
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:
-
At the very start:
-
If request’s target IP address space is not null, then set makeCORSPreflight to true.
-
-
Immediately after running CORS-preflight fetch:
-
If preflightResponse is a network error:
-
If preflightResponse’s IP address space is null, return preflightResponse.
-
Set request’s target IP address space to preflightResponse’s IP address space.
-
Return the result of running HTTP-no-service-worker fetch given fetchParams.
-
-
-
Immediately after running HTTP-network-or-cache fetch:
-
If response is a network error and response’s IP address space is non-null, then:
-
Set request’s target IP address space to preflightResponse’s IP address space.
-
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.
-
-
The CORS-preflight fetch algorithm is adjusted to handle the new headers:
-
Immediately before running HTTP-network-or-cache fetch:
-
If request’s target IP address space is not null, then:
-
Set "
Access-Control-Request-Local-Network
" to "true
" in preflight’s header list.
-
-
-
Immediately after the CORS check:
-
If request’s target IP address space is not null, then:
-
Let allow be the result of extracting header list values given "
Access-Control-Allow-Local-Network
" and response’s header list. -
If allow is not "
true
", return a network error.
-
-
-
-
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:
-
A new IP address space property (null or an IP address space) is added to each cache entry.
-
This new property is initialized by the create a new cache entry algorithm from request’s target IP address space.
-
This new property is checked by the cache entry match algorithm:
-
entry’s IP address space is equal to request’s target IP address space.
-
-
3.1.3. Forbidden header names
A new entry is added to the list of forbidden header names: Access-Control-Request-Local-Network
.
The user agent should have full control over this header, just as it does over other CORS headers.
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. [Issue #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:
-
A new IP address space property is added to the policy container struct.
-
It is initially public.
-
-
An additional step is added to the clone a policy container algorithm:
-
Set clone’s IP address space to policyContainer’s IP address space.
-
-
An additional step is added to the create a policy container from a fetch response algorithm:
-
Set result’s IP address space to response’s IP address space.
-
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 loopback 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 loopback.
3.4. 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.
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 local or loopback address spaces would then be a local network request.
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]
4. Implementation Considerations
4.1. Where do file
URLs fit?
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 loopback, as they seem to be just as much a part of the loopback
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.
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 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.
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 algoritms above, since that check is only applied in HTTP-network fetch.
Specify and explain Chromium’s behavior here. [Issue #75]
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 has encountered many stumbling blocks on the way to shipping parts of this specification.
In particular, shipping restrictions on fetches from non-secure contexts in the local IP address space to the loopback IP address space has proven particularly difficult, for a lower payoff. Indeed, exploiting such fetches requires attackers to already have a foothold in the local network, which substantially raises attack difficulty. As a result, Chromium exempted these fetches from restrictions temporarily, choosing to focus on fetches from the public IP address space.
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 local or loopback 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 local IP addresses which only make sense on the user’s local 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. [Issue #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 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.5. 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.
This opens the door to cross-network attacks:
-
A user connects to two different local networks: a home Wi-Fi network and a corporate VPN. Their smart fridge has been hacked. They open their smart fridge’s web interface, which then performs CSRF attacks against corporate websites accessible via the VPN.
-
A user connects to a malicious internet cafe Wi-Fi, which requires users to keep a captive portal page open. They close their laptop, go home, open up their laptop again. The captive portal page (either still open or reloaded from cache as the user agent restores its previous state) performs CSRF attacks against the user’s home devices.
-
A user connects to a malicious internet cafe Wi-Fi, whose captive portal website caches a malicious script from
http://router.example/popular-library.js
(the cafe network administrator operates a malicious DNS server) with a very long expiry. The user powers their computer off, goes home, boots up their computer again and visits their router’s administration interface athttp://router.example
, which embeds/popular-library.js
. The malicious script is loaded in the administration interface’s first-party context.
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. [Issue #28]
5.6. HTTP cache
5.6.1. Applying checks to subresources
The following is no longer accurate. Implementation experience revealed that integrating with the cache was useful even in protecting network resources against CSRF attacks. This section needs to be rewritten. [Issue #75]
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 Local 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 local 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.
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. 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.