New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix for off-path migration attack #2033
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1751,12 +1751,15 @@ the one to which the PATH_CHALLENGE was sent, path validation is considered to | |
have failed, even if the data matches that sent in the PATH_CHALLENGE. | ||
|
||
Additionally, the PATH_RESPONSE frame MUST be received on the same local address | ||
from which the corresponding PATH_CHALLENGE was sent. If a PATH_RESPONSE frame | ||
is received on a different local address than the one from which the | ||
PATH_CHALLENGE was sent, path validation is considered to have failed, even if | ||
the data matches that sent in the PATH_CHALLENGE. Thus, the endpoint considers | ||
the path to be valid when a PATH_RESPONSE frame is received on the same path | ||
with the same payload as the PATH_CHALLENGE frame. | ||
from which the corresponding PATH_CHALLENGE was sent. An endpoint considers the | ||
path to be valid when a PATH_RESPONSE frame is received on the same path with | ||
the same payload as the PATH_CHALLENGE frame. | ||
|
||
If a PATH_RESPONSE frame is received on a different local address than the one | ||
from which the PATH_CHALLENGE was sent, path validation is not considered to be | ||
successful, even if the data matches the PATH_CHALLENGE. This doesn't result in | ||
path validation failure, as it might be a result of a forwarded packet (see | ||
{{off-path-forward}}) or misrouting. | ||
|
||
|
||
## Failed Path Validation | ||
|
@@ -1766,7 +1769,8 @@ abandons its attempt to validate the path. | |
|
||
Endpoints SHOULD abandon path validation based on a timer. When setting this | ||
timer, implementations are cautioned that the new path could have a longer | ||
round-trip time than the original. | ||
round-trip time than the original. A value of three times the current | ||
Retransmittion Timeout (RTO) as defined in {{QUIC-RECOVERY}} is RECOMMENDED. | ||
|
||
Note that the endpoint might receive packets containing other frames on the new | ||
path, but a PATH_RESPONSE frame with appropriate data is required for path | ||
|
@@ -1891,7 +1895,7 @@ After verifying a new client address, the server SHOULD send new address | |
validation tokens ({{address-validation}}) to the client. | ||
|
||
|
||
### Handling Address Spoofing by a Peer {#address-spoofing} | ||
### Peer Address Spoofing {#address-spoofing} | ||
|
||
It is possible that a peer is spoofing its source address to cause an endpoint | ||
to send excessive amounts of data to an unwilling host. If the endpoint sends | ||
|
@@ -1914,7 +1918,7 @@ If an endpoint skips validation of a peer address as described in | |
{{migration-response}}, it does not need to limit its sending rate. | ||
|
||
|
||
### Handling Address Spoofing by an On-path Attacker {#on-path-spoofing} | ||
### On-Path Address Spoofing {#on-path-spoofing} | ||
|
||
An on-path attacker could cause a spurious connection migration by copying and | ||
forwarding a packet with a spoofed address such that it arrives before the | ||
|
@@ -1938,6 +1942,55 @@ Note that receipt of packets with higher packet numbers from the legitimate peer | |
address will trigger another connection migration. This will cause the | ||
validation of the address of the spurious migration to be abandoned. | ||
|
||
|
||
### Off-Path Packet Forwarding {#off-path-forward} | ||
|
||
An off-path attacker that can observe packets might forward copies of genuine | ||
packets to endpoints. If the copied packet arrives before the genuine packet, | ||
this will appear as a NAT rebinding. Any genuine packet will be discarded as a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be more precise in describing the attack:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that we need a precise description of the attack. This is already far too many words. |
||
duplicate. If the attacker is able to continue forwarding packets, it might be | ||
able to cause migration to a path via the attacker. This places the attacker on | ||
path, giving it the ability to observe or drop all subsequent packets. | ||
|
||
Unlike the attack described in {{on-path-spoofing}}, the attacker can ensure | ||
that the new path is successfully validated. | ||
|
||
This style of attack relies on the attacker using a path that is approximately | ||
as fast as the direct path between endpoints. The attack is more reliable if | ||
relatively few packets are sent or if packet loss coincides with the attempted | ||
attack. | ||
|
||
A non-probing packet received on the original path that increases the maximum | ||
received packet number will cause the endpoint to move back to that path. | ||
Eliciting packets on this path increases the likelihood that the attack is | ||
unsuccessful. Therefore, mitigation of this attack relies on triggering the | ||
exchange of packets. | ||
|
||
In response to an apparent migration, endpoints MUST validate the previously | ||
active path using a PATH_CHALLENGE frame. This induces the sending of new | ||
packets on that path. If the path is no longer viable, the validation attempt | ||
will time out and fail; if the path is viable, but no longer desired, the | ||
validation will succeed, but only results in probing packets being sent on the | ||
path. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is all heuristics, we may want to be a bit more explicit. If the destination CID in the packet is new, then the migration is more likely to be voluntary. If the address family is IPv6, NAT rebinding is not expected quite as much, even with NAT66, since NAT66 is very unlikely to run out of address space. If the last rebinding happened fewer than X seconds ago, this is probably very suspicious. Etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point here is that this isn't heuristics-based decision-making. It's currently arranged to be mechanical. The only discretionary part is the rate at which probes are generated. I think that the right thing to do is acknowledge that heuristics might be used to make this defense more robust and to mention some of the things you point to. |
||
An endpoint that receives a PATH_CHALLENGE on an active path SHOULD send a | ||
non-probing packet in response. If the non-probing packet arrives before any | ||
copy made by an attacker, this results in the connection being migrated back to | ||
the original path. Any subsequent migration to another path restarts this | ||
entire process. | ||
|
||
This defense is imperfect, but this is not considered a serious problem. If the | ||
path via the attack is reliably faster than the original path despite multiple | ||
attempts to use that original path, it is not possible to distinguish between | ||
attack and an improvement in routing. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, yeah, except if the improvement in routing only last for a short while, and after that the connection is dropped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't the connection simply move back to the original path (with a higher RTT) once the attacker stops racing the packets? So I think that calling the attacker an "improvement in routing" is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, since there will still be duplicate packets from the client and you can't make the client actually change the remote address that it sends to. So even if the attack succeeds, if the attacker stops delivering packets (or even starts delivering them more slowly), I think you would automatically just go back to the original path (as long as the client is still sending). My understanding here is that this overall change is mostly necessary to prompt traffic from a client that might otherwise have quiesced. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The risk is that the attacker can convince a peer that the connection is dead, meaning that no more packets are sent. The interaction between validation timers and dead path detection probably need some work. |
||
An endpoint could also use heuristics to improve detection of this style of | ||
attack. For instance, NAT rebinding is improbable if packets were recently | ||
received on the old path, similarly rebinding is rare on IPv6 paths. Endpoints | ||
can also look for duplicated packets. Conversely, a change in connection ID is | ||
more likely to indicate an intentional migration rather than an attack. | ||
|
||
|
||
## Loss Detection and Congestion Control {#migration-cc} | ||
|
||
The capacity available on the new path might not be the same as the old path. | ||
|
@@ -1963,13 +2016,16 @@ multiple paths will still send ACK frames covering all received packets. | |
|
||
While multiple paths might be used during connection migration, a single | ||
congestion control context and a single loss recovery context (as described in | ||
{{QUIC-RECOVERY}}) may be adequate. A sender can make exceptions for probe | ||
packets so that their loss detection is independent and does not unduly cause | ||
the congestion controller to reduce its sending rate. An endpoint might set a | ||
separate timer when a PATH_CHALLENGE is sent, which is cancelled when the | ||
corresponding PATH_RESPONSE is received. If the timer fires before the | ||
PATH_RESPONSE is received, the endpoint might send a new PATH_CHALLENGE, and | ||
restart the timer for a longer period of time. | ||
{{QUIC-RECOVERY}}) may be adequate. For instance, an endpoint might delay | ||
switching to a new congestion control context until it is confirmed that an old | ||
path is no longer needed (such as the case in {{off-path-forward}}). | ||
|
||
A sender can make exceptions for probe packets so that their loss detection is | ||
independent and does not unduly cause the congestion controller to reduce its | ||
sending rate. An endpoint might set a separate timer when a PATH_CHALLENGE is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...which makes sense, given that you don't retransmit PATH_CHALLENGE or PATH_RESPONSE, maybe we should note that here or nearby as well? |
||
sent, which is cancelled when the corresponding PATH_RESPONSE is received. If | ||
the timer fires before the PATH_RESPONSE is received, the endpoint might send a | ||
new PATH_CHALLENGE, and restart the timer for a longer period of time. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought we'd already addressed this elsewhere, but I can't find it anymore, so this looks good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just the old text, with the paragraph split to allow for the new text. If you have a suggestion here, that would be great, but I can't figure out how to add that without doing a lot of damage to the existing text. |
||
|
||
|
||
## Privacy Implications of Connection Migration {#migration-linkability} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won’t work out well when switching from a 10ms WiFi.
I don’t think any value derived from the current RTT helps here. We don’t have any information about the new path, so I suggest we define a constant duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same thought, or a minimum value of sorts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered that, but we still have a minimum on the RTO, which should cover this case well enough.