如何处理套接字中的网络端口滥用
How to deal with network port abuse in sockets
我有一个用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攻击,您最终会耗尽套接字。
然后还有管理打开的套接字的问题:
- 不要关闭它。打开的套接字永远存在。攻击者的代价:高-他们没有鳍。我的代价:更高。我所有的文件描述符最终都会被使用。
- 为每个套接字生成一个线程以休眠10分钟,然后关闭套接字。攻击者的代价:高-他们没有鳍。我的代价:更高。虽然我最终关闭了套接字,但对于每个请求,我的套接字使用的时间都比攻击者长,并且我有一个线程的开销。 生成一个线程来处理即将过期的所有滥用套接字。攻击者的代价:高-他们没有鳍。我的代价:更高。比如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/failChrome至少会挂在那里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
,让他们使用合适的套接字。
- 在不知道套接字的情况下关闭网络连接
- 在网络套接字计时器滴答后增加asio短读错误
- 提升网络套接字接受挂起
- C++和网络套接字
- POCO 网络套接字异常
- 是否可以寻址另一个网络中的服务器/客户端套接字?(C++)
- 使用 Boost.Asio 将 UDP 套接字绑定到特定网络接口
- 使用 UDP 协议从 Windows 套接字发送到 Qt 套接字的网络数据包上的结构编码和解码
- 如何在提升野兽网络套接字中传递模型类型
- 如何确定网络堆栈何时准备好再次打开到同一主机/端口的套接字?
- 野兽网络套接字惯用关机
- 网络:当接口打开/关闭时,在所有接口上绑定套接字
- 通过网络中的套接字进行文件传输时,该程序中的文件传输错误是什么
- 使用C 中的套接字通过网络发送音频数据
- 使用 SFML C++库在 UDP 网络中进行多套接字"握手"
- 网络tcp套接字应用程序重试方法
- 如何使用套接字构建健壮的网络体系结构
- 使用 C/C++ 套接字模拟网络状况
- C或C++库,用于对网络套接字帧进行编码和解码
- c++网络/套接字编程