-
Notifications
You must be signed in to change notification settings - Fork 25
Description
I don't understand the IP address classification rules. Why are link-local addresses "local" while private IPv4 addresses are "private"? In a normal, dual-homed home network, you'd address devices on the network through fe80::*
addresses for IPv6 (classified as "local") but through 192.168.*.*
or whatever for IPv4 (classified as "private"). If semantically, "local" is supposed to mean "this machine" while "private" is supposed to mean "the local network", then the current classification of IPv6 addresses is broken.
Also note that with IPv6, there are one (or potentially even more) global addresses using which devices on the local network are reachable at any given time. (Potentially multiple ones with the same prefix for privacy reasons, but I believe there can also be ones with different prefixes if the ISP recently delegated a new IPv6 prefix to the router, or if there are multiple routers with upstream connectivity.) And ISPs will sometimes delegate a /64
, other times an entire /48
(from which the user can then carve multiple /64
networks for different private network segments). (And which IPv6 addresses belong to the local network can change dynamically if the user connects to a wifi or whatever.)
And a completely different can of worms is that if the user e.g. configures their device such that it will connect both to some open wifi network and some corporate wifi network, a private IP address isn't even semantically same-origin with itself across time, because in one case it is some trusted corporate server while in the other case it's routed through an attacker-controlled network.)
With static network config
So here's a list of brainstormed things that I think might help make these rules vaguely solid under the assumption that we're not moving between networks:
- Clarify that
fe80::/10
in a DNS response must always error out, as explained in RFC 4472, section 2.1:Link-local addresses should never be published in DNS (whether in forward or reverse tree), because they have only local (to the connected link) significance
- If the browser wants to permit the use of link-local addresses such as
http://[fe80::1%eth0]:80/
(which are only valid on a single machine, because they incorporate information about the client's network interface names), classifyfe80::
as "private" such that "local" refers exclusively to services running on the local machine. This should fix the classification of "private" vs everything else.- Chrome does not currently support this, from what I can tell. Firefox doesn't seem to support it either. E.g.
curl
seems to support it, though. It would be a useful feature if someone wants to have stable browser bookmarks referencing IPv6 devices on the local network. - With the caveat that VMs running on the local machine would still not be classed as "private" even if nothing on the local network can talk to them directly. So VMs might not get the protection they deserve. This might be fixed if we isolated "private" addresses by network interface - see the section on network switching.
- Same thing if there are services on the local machine that bind to global addresses but are isolated using firewall rules, but we probably don't care about that too much. We can't really reason about local firewall rules, and nobody should be doing this if they don't want the service to be reachable from the network.
- Chrome does not currently support this, from what I can tell. Firefox doesn't seem to support it either. E.g.
- When initiating a connection to a machine with a global address, first query the local routing table for that address; if the address is routed without a router, it should probably be classified as "private".
- Unless the interface is directly connected to the public internet without any local router. The best heuristic I can think of for this is "do we have a non-private IPv4 address".
- This would probably still fail unsafe if the user e.g. plugged a layer 3 wifi AP into their home router and the current machine is on the wifi network; in that scenario, the wifi AP and the main router would have separate
/64
address prefixes. We could try to detect such situations by using an IPv4 traceroute to detect at which TTL the edge from the local network to the internet is, then hope that the route to the internet has the same length for IPv6?- E.g. the Google WiFi documents that it will grab its own IPv6 prefix on the WAN port unless it is configured to run in bridge mode. They call this scenario "Double NAT".
- Arguably we could still theoretically get into a situation where the router knows more global prefixes for the local network than we do. We could try to detect that by sending a packet with TTL 2 (which will be the first machine of the ISP on a typical home network; but see previous bullet point) and checking whether that results in an ICMP
Time Exceeded
message from the same gateway as when trying to contact some IPv6 address that is known to be publicly routed.
With network switching
If we are switching between networks (one of which is potentially malicious, especially if the client device is a laptop or smartphone that is configured to connect to open wifi networks), things around "private" addresses immediately get really ugly. Imagine an attacker who forces us to disconnect from the trusted company wifi, causing us to connect to the attacker's spoofed open wifi; then on the open wifi, the attacker navigates some iframe to http://192.168.1.1/
, loads malicious script in the frame, then terminates the open wifi and lets us connect back to the trusted company wifi, on which attacker-controlled script now runs in the origin of http://192.168.1.1/
.
To handle this properly, it might be necessary to do something along the lines of:
- The browser associates connections with network identifiers; the network identifier includes at least the interface name over which the packets will be routed and, if it is a wifi interface, something like the SSID.
- This would ideally need an interface from the OS that says "synchronously kill this socket if its routing changes or the interface is about to associate with a different network".
- When "private" addresses try to interact with each other across network identifiers, the same checks as for
public -> private
interactions apply. - Network identifiers are part of the origin. Even if protocol, host, port, and IP address all match, documents will be cross-origin if their network identifiers don't match. Cookie stores and such are also split on network identifiers.
- If a request is made where protocol+host+port+ip match, but the network identifiers don't match, the request should be BLOCKED unless BOTH of the involved origins have opted in - a page loaded over a trusted network should not send XHR requests with CSRF tokens to an untrusted network or load scripts from an untrusted network, no matter what headers a server on the untrusted network sets.
If this is considered out of scope, there should probably at least be a note somewhere that points out this scenario.