【问题标题】:How to deal with network port abuse in sockets如何处理套接字中的网络端口滥用
【发布时间】:2012-12-18 05:52:42
【问题描述】:

我有一个使用标准网络套接字上的 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 一样,许多套接字保持打开状态。管理它的单个线程的开销。代码复杂,烦人。

【问题讨论】:

  • 你想要的是关闭连接而不发送 FIN。然而,这并不容易实现。我在 SO 上找到的一些已经是这样的:stackoverflow.com/questions/9925705/…
  • 另一个选择是创建一个蜜罐。只是不要关闭套接字,而是让它挂很长时间。它只会占用一个套接字资源(这会是个问题吗?)。大约 10 分钟后释放它。
  • 在最近的 linux 上,您可以使用套接字修复模式 (TCP_REPAIR)。然后关闭套接字;不会发送任何 FIN,所有内容都将被清除。不过有点矫枉过正。
  • 如果你想阻止来自该 IP 地址的连接,你可以添加一个 iptables 规则。 IP 集是一种内核功能,旨在用于此类内容。
  • 让套接字打开是次优的。

标签: c++ c sockets networking tcp


【解决方案1】:

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

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

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

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

【讨论】:

  • +1 建议在防火墙级别进行阻止并提及 ISP 委托动态 IP 地址。
  • 防火墙阻止他是个好主意,也应该这样做。但在某些情况下,这还不够。正如您所说的 DDOS 攻击,您会收到来自各地 IP 的一些请求。
  • 除了尝试识别僵尸并在您控制的最远路由器上阻止它们之外,您无能为力对抗大规模 DDOS 攻击。但是请随时向 serverfault.com 上的人询问他们的 DDOS 对策。
【解决方案2】:

使用这个:

#include <sys/socket.h>

int shutdown(int socket, int how);

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

【讨论】:

    【解决方案3】:

    当您检测到abuse

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

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

    【讨论】:

      【解决方案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。

      见识见识!

      【讨论】:

      • 很好的研究,与shutdown 方法相比仍然存在一些开销。
      猜你喜欢
      • 2018-05-18
      • 1970-01-01
      • 2014-11-07
      • 1970-01-01
      • 2013-05-12
      • 2021-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多