如何处理套接字中的网络端口滥用

How to deal with network port abuse in sockets

本文关键字:网络 套接字 何处理 处理      更新时间:2023-10-16

我有一个用c++编写的web应用程序,使用TCP/IP通过标准网络套接字,在Linux上运行。

我定期从运行自动脚本的垃圾邮件发送者那里收到大量的滥用请求。我可以检测到这些并关闭插座。现在我只是做了一个礼貌的套接字关闭,就像我对任何已经完成的有效请求所做的那样,使用像这样的套接字库关闭:

close( mSocket );

但有时关闭套接字通常会通知垃圾脚本套接字连接已经终止,他们立即发起另一个欺诈性请求。

终止TCP/IP连接的最佳方法是什么?该连接清除了系统上打开的套接字,但使远程方处于挂起状态。也就是说,我想以对我来说成本最低,但对他们来说成本最高的方式关闭一个套接字。

@Nicholas威尔逊:

使用TCP_REPAIR似乎是一个好主意。在TCP_REPAIR模式下关闭套接字时,不发送FIN或RST报文。远端套接字处于挂起状态。我要试一试,然后回来报告。下面是我的(未经测试的)代码:

if ( abuse )
{
   int aux = 1;
   if ( setsockopt( mSocket, SOL_TCP, TCP_REPAIR, &aux, sizeof( aux )) < 0 )
      reportError( "Tried to do a rude socket close... but could not turn on repair mode.n" );
}
close( mSocket );

我将报告如果这工作。(@编辑:下面的测试答案)

@"保持套接字打开"的想法:

这可以工作,但不是最优的。攻击者有能力用打开的套接字使您的系统饱和。每个请求都创建一个新的套接字,该套接字保持打开状态。使用DOS攻击,您最终会耗尽套接字。

然后还有管理打开的套接字的问题:

  1. 不要关闭它。打开的套接字永远存在。攻击者的代价:高-他们没有鳍。我的代价:更高。我所有的文件描述符最终都会被使用。
  2. 为每个套接字生成一个线程以休眠10分钟,然后关闭套接字。攻击者的代价:高-他们没有鳍。我的代价:更高。虽然我最终关闭了套接字,但对于每个请求,我的套接字使用的时间都比攻击者长,并且我有一个线程的开销。
  3. 生成一个线程来处理即将过期的所有滥用套接字。攻击者的代价:高-他们没有鳍。我的代价:更高。比如2,很多套接字都是打开的。单个线程管理它的开销。代码复杂,烦人。

好的,做了一些研究,我有一个适合我的答案,基于TCP_REPAIR。这比我一开始想的要复杂一点:

if ( abuse )
{
   // read some bytes from the spammer - to establish the connection
   u32 tries = 20;
   while ( tries )
   {
      sleep( 1000 );
      char tmpBuf[32];
      s32 readCount = recv( mSocket, &tmpBuf[0], 32, 0 );
      if ( readCount > -1 ) break;
      tries--;
   }
#ifdef TCP_REPAIR
   int aux = 1;
   if ( setsockopt( mSocket, SOL_TCP, TCP_REPAIR, &aux, sizeof( aux )) < 0 )
   {
     reportError( "could not turn on repair mode" );
   }
#else // !TCP_REPAIR
   // no TCP REPAIR - best we can do is an abort close
   struct linger so_linger;
   so_linger.l_onoff = 1;
   so_linger.l_linger = 0;
   if ( setsockopt( mSocket, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger ) < 0  )
   {
      reportError( "Cannot turn off SO_LINGER" );
   }
#endif // TCP_REPAIR
}
close( mSocket );

在内核级别,如果你关闭一个连接,TCP栈将发送FIN或RST数据包,不管你是怎么做的(close或shutdown)。无论哪种方式,攻击者都会收到您已关闭连接的通知。

我们想默默地关闭连接,让他们等着发现你没有回答…因为我们有报复心。

TCP_REPAIR是一个新的套接字API,旨在允许您"冻结"套接字,保存其状态,并在另一个进程甚至另一个系统上重新加载套接字状态。在正常使用情况下,客户端永远不会知道他们的连接被转移到其他地方。

但是我们可以滥用这个API,我们将套接字置于修复模式,但不保存它的状态,也不恢复它。当我们以修复模式关闭套接字时,它会被静默删除。

这适用于已经开始的滥用请求。也就是说,我们已经阅读了垃圾邮件发送者的请求,并确定这是欺诈行为,TCP_REPAIR将其杀死。

但是如果你通过IP阻止请求,在连接之后,没有首先读取套接字,不知何故远程方被通知。他们得到了RST。或者可能连接中的某些内容从未完全完成,远程系统几乎立即终止了请求。

所以我们首先从黑客的套接字中读取几个字节。在我的例子中,套接字已经处于非阻塞模式。但是如果不是,您希望将套接字设置为非阻塞,否则您将自己暴露给黑客打开连接,但不发送数据包,并使服务器挂起-就像您计划对他所做的那样。如果几微秒后你没有收到数据包,你还是会关闭它。

但是如果你从他那里读了几个字节,那么他的程序就会等待你的响应,而这个响应永远不会到来。

TCP_REPAIR仅在Linux内核3.5及以上版本上可用。在此之下,我能做的最好的事情就是一个"脏"的套接字关闭。在这种情况下,不是给他发送FIN,而是给他发送RST。在他看来,根本就没有建立起有效的联系。要做到这一点,您可以关闭SO_LINGER,实质上中断套接字连接关闭握手,然后调用close。

像一个魅力,把你的浏览器指向这里:

http://oroboro.com/fail

Chrome至少会挂在那里5-10秒。看看我的日志,我每秒命中10次——他每10秒左右才能击中我一次。从这个:0加载到我的系统。

再见,笨蛋!

当您检测到恶意客户端时,我建议您不仅要关闭连接,还要拒绝来自同一IP地址的任何新连接。

你至少可以在你的应用程序中黑名单IP。保存一个被禁止的IP地址列表,并立即关闭来自该列表中IP的任何可接受的套接字。

但是为了保护更多的资源,最好在网络架构中进一步阻断连接。当您能够这样做时,通知网关路由器阻止它。如果不可能,请尝试让负载平衡器阻止它。如果不这样做,至少在服务器的本地防火墙中添加一个规则。

但请记住,许多这样的攻击源于消费级互联网连接(无论用户是否意识到)。这通常意味着他们的IP地址被动态地分配和定期地重新分配。几天前被垃圾邮件发送者使用的IP现在可能被合法用户使用。所以基于ip的禁令不应该永远持续下去。

使用

#include <sys/socket.h>
int shutdown(int socket, int how);

它将发送RST并立即关闭(连接)。这将使该端口看起来没有服务,并且攻击者有望停止在该端口上发送垃圾邮件。调用close()释放句柄。

当检测到abuse

有一个用于"垃圾邮件发送者"的套接字池,每当检测到abuse时,使用其中一个套接字将它们放入容器中。如果没有可用的套接字,回收旧套接字并关闭它。

如果一个连接符合!abuse,让他们使用合适的套接字。