11

我有一个使用标准网络套接字上的 TCP/IP 用 C++ 编写的 Web 应用程序,在 Linux 上运行。该服务对狂野和毛茸茸的互联网开放。

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

close( mSocket );

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

终止 TCP/IP 连接的最佳方法是什么,该连接清理了我系统上打开的套接字,但让远程方挂起。那就是我想以对我来说成本最低但对他们来说成本最高的方式关闭套接字。

@尼古拉斯·威尔逊:

使用 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 );

如果这有效,我会报告。(@edit:下面的测试答案)

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

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

然后管理打开的套接字也存在问题:

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

4 回答 4

4

好的,做了一些研究,我有一个基于 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 数据包,无论您如何操作(关闭或关闭)。无论哪种方式,都会通知攻击者您关闭了连接。

我们想默默地关闭连接,让他们等待意识到你没有回答......因为我们是报复性的。

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。

见傻子!

于 2013-01-04T21:10:04.293 回答
3

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

您至少可以将应用程序中的 IP 列入黑名单。保留一份禁止 IP 地址的列表,并立即关闭任何源自该列表中 IP 的已接受套接字。

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

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

于 2013-01-04T14:32:33.703 回答
1

用这个:

#include <sys/socket.h>

int shutdown(int socket, int how);

它将RST立即发送并关闭(连接)。这会让人觉得该端口上没有服务,攻击者有望停止在该端口上发送垃圾邮件。呼叫close()释放手柄。

于 2013-01-04T14:34:50.860 回答
0

当你检测到一个abuse

abuse每当您检测到您使用其中一个插槽将它们放入锅中时,都会为“垃圾邮件发送者”提供一个插槽池。如果没有可用的空闲套接字,则回收最旧的,然后关闭并关闭它。

如果连接符合条件,则!abuse让他们使用适当的套接字。

于 2013-01-04T18:10:54.210 回答