不发送RST的情况下关闭一个打开的TCP连接

4

阅读nginx: 忽略一些没有正确Host头的请求,让我想到实际上不可能通过close(2)关闭TCP连接而不经过操作系统发送RST(和/或FIN)来终止底层TCP连接。

一个解决方法是使用类似tcpdrop(8)的东西,然而,正如在OpenBSD上的usr.sbin/tcpdrop/tcpdrop.cFreeBSD中所看到的那样,它是通过基于sysctl的接口实现的,并且可能在BSD之外存在可移植性问题。(事实上,看起来即使基于sysctl的实现在OpenBSD和FreeBSD之间也有足够的差异需要一个移植层——OpenBSD使用 tcp_ident_mapping 结构(随后包含两个sockaddr_storage元素,以及其他一些信息),而FreeBSDDragonFlyNetBSD直接使用两个sockaddr_storage元素的数组。)事实证明,OpenBSD的tcpdrop似乎确实会像tcpdump(8)一样发送R数据包,这可以通过查看/sys/netinet/tcp_subr.c :: tcp_drop()得到确认,该函数最终调用tcp_close()(并且tcp_close()在SO的其他地方已经确认会发送RST),因此,看起来它甚至都不会起作用。

如果我通过C自己建立连接,是否有一种方法可以在不向另一方发送确认信息的情况下随后断开连接,例如,不发起RST?

那样做的意义是什么?顺便说一下,您提供的工具实际上是一种管理关闭连接的方式。它确实会发送通知。 - spectras
1
连接也会挂在你的端上,以及所有的网络设备上。适当地发送关闭通知可以让你的路由器和防火墙将连接放在快速老化线上,在它们的表中释放宝贵的条目。假设攻击者拥有比你更多的设备,那么你实际上会给你的基础设施带来比他们更大的压力(更准确地说,压力在他们的一侧是分散的)。 - spectras
@spectras 你说得对 - 刚在OpenBSD上测试了tcpdrop; 确实会发送一个 RST - cnst
你会浪费时间去追求这个。基于我所解释的原因,很难找到现成的工具来完成这个任务。如果垃圾邮件制造者让你担忧,明智之举不是跳过发送一个小数据包,而是确保阻止进一步的连接尝试,他们将消耗大量的CPU和内存使用、污染你的日志文件的数十个更大的数据包。更不用说他们还可以利用机会调整尝试的策略直到绕过你的安全措施。 - spectras
@ spectras,阻止整个IP几乎不是一个好的选择 - 如果它是一个主要ISP的坏用户,他们正在使用有限的一组IP地址进行CGN呢?或者有人故意发出请求以使他们的整个代理被屏蔽服务?编程不仅是为了解决业务需求,也是为了娱乐;我认为这种静默丢弃TCP的情况是满足这两种用例的问题之一。 - cnst
显示剩余6条评论
2个回答

1
欺骗攻击者可能是个好主意。当然,在这种情况下,您已经为已建立的连接保留了服务器资源。在最基本的模式下,您可以使用netfilter来拦截任何带有RST或FIN标志的TCP出站段。以下iptables规则可能是一个例子:
sudo iptables -A OUTPUT -p tcp --tcp-flags FIN,RST SYN -j DROP

当然,这个规则将影响到你所有的TCP连接。我写这篇文章只是为了提供一些思路,让你知道如何实现它。请前往https://www.netfilter.org/获取更多关于解决方案的想法。基本上,你应该能够仅针对所选连接执行相同的操作。
由于TCP的工作方式,如果你能够实现它,客户端(或攻击者)将会保持长时间的连接。要了解其对客户端的影响,请阅读此处:TCP, recv function hanging despite KEEPALIVE。在我的配置中,需要13分钟才能使套接字进入错误状态(这取决于Linux参数,如tcp_retries1和tcp_retries2)。
考虑到 DoS 攻击通常涉及来自数千个不同设备的连接,而不一定是来自同一设备的多个连接。这很容易在防火墙中检测和阻止。因此,你很难在客户端中产生资源耗尽。此外,这种解决方案无法解决半开放连接攻击的情况。

这个解决方案不仅完全不完整,而且还是针对Linux特定的。如果仅在防火墙层面上完成,而没有适当地通过多数据包的FIN序列(例如,没有将RST插入到本地服务器或其他类似方式)终止Web服务器期望的连接,则这实际上也会导致您自己的Web服务器遭受拒绝服务攻击。 - cnst
@cnst,这个想法是Web服务器在其端关闭连接。但带有FIN和RST的TCP段不会到达客户端。你说的是服务器套接字将永远等待FIN/ACK吗?是的,iptables和netfilter是Linux特定的。你认为响应不完整的原因是什么? - rodolk

1
如果我通过C自己建立连接,有没有一种方法可以在不向对方发出确认的情况下随后断开它,例如,不启动RST? 没有。即使有,如果对等方随后发送任何内容,都将被回答为RST。 注意:正常的TCP终止使用FIN,而不是RST。

这个想法是浪费攻击者的资源;此外,我认为是否承认没有正确建立状态的通信取决于防火墙设置。 - cnst
当然这取决于防火墙,但您的问题是关于已建立的连接,这些连接已经穿透了防火墙。如果现在您只是询问防火墙规则,(a)那么您已经完全改变了问题;(b)并且离题了。让攻击者浪费资源而不消耗您自己的最好方法是首先不允许连接,通过防火墙拒绝所有内容。 - user207421
不,问题是一样的——防火墙在内核中运行;我想断开连接,这将完全从内核中删除它,包括从防火墙中删除,而不发送任何 RST 回来;如果客户端尝试再次通过相同的连接进行通信,就好像从未建立过连接一样,因此防火墙将不会发送任何回复;同样的问题,与防火墙规则无关。 - cnst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接