我有一个用c ++编写的web应用程序,使用TCP / IP通过在Linux上运行的标准网络套接字。该服务对野生和羊毛互联网开放。
我会定期收到来自运行自动脚本的垃圾邮件发送者的滥用请求。我可以检测到这些并关闭套接字。现在我只是做一个礼貌的套接字关闭,就像我对已经完成的任何有效请求所做的那样,套接字lib接近这样:
close( mSocket );
但有时关闭套接字通常会通知垃圾邮件脚本套接字连接已终止,并立即发起另一个欺诈请求。
终止TCP / IP连接的最佳方法是什么,它可以清理系统上的打开套接字,但会使远程方挂起。这就是我希望以对我来说成本最低的方式关闭套接字,但是它们的成本最高。
@Nicholas Wilson:
使用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攻击,你最终会用尽套接字。
然后管理开放套接字也存在问题:
答案 0 :(得分: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。或者可能连接中的某些内容从未完全完成,远程系统几乎立即中止请求。
所以我们先从黑客的socket读取几个字节。在我的情况下,套接字已经处于非阻塞模式。但是如果不是你想要将套接字设置为非阻塞,或者你打开自己的黑客开放连接,但不发送数据包并让你的服务器挂起 - 就像你打算对他做的那样。如果在几微秒之后你没有得到一个数据包,你就会把他关闭。
但如果你从他那里读了几个字节,那么他的程序就会等待你的回复永远不会来。
TCP_REPAIR仅适用于Linux Kernel 3.5及更高版本。在那之下,我能做的最好的是一个'脏'插座关闭。这是他送你和RST而不是给他发送FIN的地方。它看起来像是从未建立过有效的连接。为此,关闭SO_LINGER,基本上打破套接字连接关闭握手,然后调用close。
像魅力一样,将浏览器指向此处:
Chrome至少会在那里停留5-10秒。看看我的日志,我每秒得到10次点击 - 他只能每10秒左右打我一次。从我的系统加载:0。
见ya sucker!
答案 1 :(得分:3)
当您检测到恶意客户端时,我建议您不要关闭连接,还要拒绝来自同一IP地址的任何新连接。
至少可以做的是将应用程序中的IP列入黑名单。保留一个禁止的IP地址列表,并立即关闭任何来自该列表上IP的接受套接字。
但是为了保护更多资源,在网络架构中进一步阻止连接会更好。如果能够这样做,请通知网关路由器阻止它。如果那是不可能的,请尝试让负载均衡器阻止它。如果做不到这一点,至少要将规则添加到服务器的本地防火墙。
但请记住,许多此类攻击来自消费级互联网连接(无论用户是否知晓)。这通常意味着他们的IP地址被分配并定期动态重新分配。几天前垃圾邮件发送者使用的IP现在可能被合法用户使用。因此,基于IP的禁令不应该永远存在。
答案 2 :(得分:1)
使用此:
#include <sys/socket.h>
int shutdown(int socket, int how);
它会立即发送RST
并关闭(连接)。这似乎表明,该端口上没有服务,攻击者希望停止在该端口上发送垃圾邮件。致电close()
以释放手柄。
答案 3 :(得分:0)
当您检测到abuse
abuse
将它们放入锅中时,为“垃圾邮件发送者”设置了一个套接字池。如果没有可用的空闲套接字,则回收最旧的套接字,然后关闭并关闭它。
如果连接符合!abuse
条件,请让他们使用正确的套接字。