| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0b534b5 commit 26a30d8
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -790,22 +790,30 @@ void Endpoint::SendRetry(const PathDescriptor& options) { | |||
| 790 | 790 | ||
| 791 | 791 | void Endpoint::SendVersionNegotiation(const PathDescriptor& options) { | |
| 792 | 792 | Debug(this, "Sending version negotiation on path %s", options); | |
| 793 | - // While creating and sending a version negotiation packet does consume a | ||
| 794 | - // small amount of system resources, and while it is fairly trivial for a | ||
| 795 | - // malicious peer to force a version negotiation to be sent, these are more | ||
| 796 | - // trivial to create than the cryptographically generated retry and stateless | ||
| 797 | - // reset packets. If the packet is sent, then we'll at least increment the | ||
| 798 | - // version_negotiation_count statistic so that application code can keep an | ||
| 799 | - // eye on it. | ||
| 793 | + // A malicious peer can trivially force version negotiation packets by | ||
| 794 | + // sending packets with unsupported QUIC versions, potentially from | ||
| 795 | + // spoofed source addresses. Rate-limit per remote host to prevent | ||
| 796 | + // amplification attacks. | ||
| 797 | + const auto exceeds_limits = [&] { | ||
| 798 | + SocketAddressInfoTraits::Type* counts = | ||
| 799 | + addr_validation_lru_.Peek(options.remote_address); | ||
| 800 | + auto count = counts != nullptr ? counts->version_negotiation_count : 0; | ||
| 801 | + return count >= kMaxVersionNegotiations; | ||
| 802 | + }; | ||
| 803 | + | ||
| 804 | + if (exceeds_limits()) { | ||
| 805 | + Debug(this, "Version negotiation rate limit exceeded for %s", | ||
| 806 | + options.remote_address); | ||
| 807 | + return; | ||
| 808 | + } | ||
| 809 | + | ||
| 800 | 810 | auto packet = Packet::CreateVersionNegotiationPacket(*this, options); | |
| 801 | 811 | if (packet) { | |
| 812 | + addr_validation_lru_.Upsert(options.remote_address) | ||
| 813 | + ->version_negotiation_count++; | ||
| 802 | 814 | STAT_INCREMENT(Stats, version_negotiation_count); | |
| 803 | 815 | Send(std::move(packet)); | |
| 804 | 816 | } | |
| 805 | - | ||
| 806 | - // If creating the packet is unsuccessful, we just drop things on the floor. | ||
| 807 | - // It's not worth committing any further resources to this one packet. We | ||
| 808 | - // might want to log the failure at some point tho. | ||
| 809 | 817 | } | |
| 810 | 818 | ||
| 811 | 819 | bool Endpoint::SendStatelessReset(const PathDescriptor& options, | |
@@ -847,11 +855,27 @@ void Endpoint::SendImmediateConnectionClose(const PathDescriptor& options, | |||
| 847 | 855 | "Sending immediate connection close on path %s with reason %s", | |
| 848 | 856 | options, | |
| 849 | 857 | reason); | |
| 850 | - // While it is possible for a malicious peer to cause us to create a large | ||
| 851 | - // number of these, generating them is fairly trivial. | ||
| 858 | + // A malicious peer can trigger immediate connection close packets by | ||
| 859 | + // sending Initial packets with invalid tokens or when the server is | ||
| 860 | + // busy. Rate-limit per remote host to prevent amplification attacks. | ||
| 861 | + const auto exceeds_limits = [&] { | ||
| 862 | + SocketAddressInfoTraits::Type* counts = | ||
| 863 | + addr_validation_lru_.Peek(options.remote_address); | ||
| 864 | + auto count = counts != nullptr ? counts->immediate_close_count : 0; | ||
| 865 | + return count >= kMaxImmediateCloses; | ||
| 866 | + }; | ||
| 867 | + | ||
| 868 | + if (exceeds_limits()) { | ||
| 869 | + Debug(this, "Immediate connection close rate limit exceeded for %s", | ||
| 870 | + options.remote_address); | ||
| 871 | + return; | ||
| 872 | + } | ||
| 873 | + | ||
| 852 | 874 | auto packet = | |
| 853 | 875 | Packet::CreateImmediateConnectionClosePacket(*this, options, reason); | |
| 854 | 876 | if (packet) { | |
| 877 | + addr_validation_lru_.Upsert(options.remote_address) | ||
| 878 | + ->immediate_close_count++; | ||
| 855 | 879 | STAT_INCREMENT(Stats, immediate_close_count); | |
| 856 | 880 | Send(std::move(packet)); | |
| 857 | 881 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,6 +32,20 @@ class Endpoint final : public AsyncWrap, public Packet::Listener { | |||
| 32 | 32 | static constexpr uint64_t DEFAULT_MAX_STATELESS_RESETS = 10; | |
| 33 | 33 | static constexpr uint64_t DEFAULT_MAX_RETRY_LIMIT = 10; | |
| 34 | 34 | ||
| 35 | + // Maximum number of version negotiation packets that will be sent to a | ||
| 36 | + // given remote host within the LRU tracking window. Version negotiation | ||
| 37 | + // packets are cheap to generate but can be used as an amplification | ||
| 38 | + // vector with spoofed source addresses. | ||
| 39 | + // TODO(@jasnell): Consider making this configurable via Endpoint::Options. | ||
| 40 | + static constexpr uint64_t kMaxVersionNegotiations = 10; | ||
| 41 | + | ||
| 42 | + // Maximum number of immediate connection close packets that will be sent | ||
| 43 | + // to a given remote host within the LRU tracking window. These are sent | ||
| 44 | + // when the server is busy or a token is invalid — a malicious peer could | ||
| 45 | + // trigger a large number of them. | ||
| 46 | + // TODO(@jasnell): Consider making this configurable via Endpoint::Options. | ||
| 47 | + static constexpr uint64_t kMaxImmediateCloses = 10; | ||
| 48 | + | ||
| 35 | 49 | // Endpoint configuration options | |
| 36 | 50 | struct Options final : public MemoryRetainer { | |
| 37 | 51 | // The local socket address to which the UDP port will be bound. The port | |
@@ -397,6 +411,8 @@ class Endpoint final : public AsyncWrap, public Packet::Listener { | |||
| 397 | 411 | size_t active_connections; | |
| 398 | 412 | size_t reset_count; | |
| 399 | 413 | size_t retry_count; | |
| 414 | + size_t version_negotiation_count; | ||
| 415 | + size_t immediate_close_count; | ||
| 400 | 416 | uint64_t timestamp; | |
| 401 | 417 | bool validated; | |
| 402 | 418 | }; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments