如何在此代码中避免DOS攻击?

时间:2012-05-28 15:30:35

标签: c++ sockets asyncsocket recv ddos

我有一个用C / C ++编写的代码,如下所示:

    while(1)
{
    //Accept
    struct sockaddr_in client_addr;
    int client_fd = this->w_accept(&client_addr);
    char client_ip[64];
    int client_port = ntohs(client_addr.sin_port);
    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));

    //Listen first string
    char firststring[512];
    memset(firststring,0,512);
    if(this->recvtimeout(client_fd,firststring,sizeof(firststring),u->timeoutlogin) < 0){
        close(client_fd);
    }

    if(strcmp(firststring,"firststr")!=0)
    {
        cout << "Disconnected!" << endl;
        close(client_fd);
        continue;
    }

    //Send OK first string
    send(client_fd, "OK", 2, 0);


    //Listen second string
    char secondstring[512];
    memset(secondstring,0,512);
    if(this->recvtimeout(client_fd,secondstring,sizeof(secondstring),u->timeoutlogin) < 0){
        close(client_fd);
    }

    if(strcmp(secondstring,"secondstr")!=0)
    {
        cout << "Disconnected!!!" << endl;
        close(client_fd);
        continue;
    }

    //Send OK second string
    send(client_fd, "OK", 2, 0);


}
    }

所以,这是可以投掷的。 我在perl中编写了一个非常简单的dos脚本,删除了服务器。

#Evildos.pl
use strict;
use Socket;
use IO::Handle;
sub dosfunction
{
my $host = shift || '192.168.4.21';
my $port = 1234;
my $firststr = 'firststr';
my $secondstr = 'secondstr';
my $protocol = getprotobyname('tcp');
$host = inet_aton($host) or die "$host: unknown host";
socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";
my $dest_addr = sockaddr_in($port,$host);
connect(SOCK,$dest_addr) or die "connect() failed: $!";
SOCK->autoflush(1);
print SOCK $firststr;
#sleep(1);
print SOCK $secondstr;
#sleep(1);
close SOCK;
}

my $i;
for($i=0; $i<30;$i++)
{
&dosfunction;
}

循环30次,服务器停机。

问题是:是否存在可以避免此类攻击的方法,系统和解决方案?

编辑:recvtimeout

int recvtimeout(int s, char *buf, int len, int timeout)
     {


fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
    return -2; // timeout!
}
if (n == -1){
    return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
    }

5 个答案:

答案 0 :(得分:4)

我不认为一般来说DOS攻击有100%有效的软件解决方案;无论你做什么,总有人可以在你的网络接口上丢弃更多的数据包。

在这种特殊情况下,看起来您的程序一次只能处理一个连接 - 也就是说,在连接#1完成其事务(或定时)之前,传入的连接#2不会被处理出)。这是一个明显的阻塞点 - 攻击者必须做的就是连接到您的服务器,然后什么也不做,并且您的服务器被有效禁用(无论您的超时时间有多长)。

为避免这种情况,您需要重写服务器代码以同时处理多个TCP连接。您可以通过切换到非阻塞I / O(通过将O_NONBLOCK标志传递给fcntl()),并使用select()或poll()等来同时等待多个套接字上的I / O,或者通过产生多个线程或子进程以并行处理传入连接,或者使用异步I / O。 (我个人更喜欢第一种解决方案,但都可以在不同程度上工作)。在第一种方法中,在接受来自该IP地址的新套接字之前,执行诸如强制关闭给定IP地址的任何现有套接字之类的操作也是切实可行的,这意味着任何给定的攻击计算机最多只能绑定一个套接字。服务器一次,除非他可以访问许多客户端计算机,否则会使该人更难管理您的计算机。

您可以阅读this article以获取有关同时处理多个TCP连接的更多讨论。

答案 1 :(得分:2)

DOS和DDOS攻击的主要问题在于它们可以解决您的弱点:即可用于提供服务的内存/处理资源的内存/数量有限。即使您使用亚马逊农场等无限可扩展性(或关闭),您也可能希望限制它以避免账单进入屋顶。

在服务器级别,您主要担心的是通过强加自我保护限制来避免崩溃。例如,您可以设置您知道可以处理的最大连接数,并且只是拒绝任何其他连接。

完整的策略将包括专门的材料,如防火墙,但总有一种方法可以播放它们,你将不得不忍受它。

例如讨厌的攻击,请阅读维基百科上的Slow Loris

  

Slowloris尝试保持与目标Web服务器的许多连接打开,并尽可能长时间打开它们。它通过打开与目标Web服务器的连接并发送部分请求来实现此目的。它会定期发送后续HTTP标头,添加到 - 但永远不会完成 - 请求。受影响的服务器将保持这些连接打开,填充其最大并发连接池,最终拒绝来自客户端的其他连接尝试。

DOS攻击有很多种,所以具体答案很难。

答案 2 :(得分:1)

这对于DOS攻击来说并非万能药,但使用非阻塞套接字肯定有助于实现可扩展性。如果你可以扩展,你可以缓解许多DOS攻击。此设计更改包括将接受调用中使用的侦听套接字和客户端连接套接字设置为非阻塞。

然后,不是在recv(),send()或accept()调用上阻塞,而是阻止poll,epoll或select调用 - 然后尽可能多地处理该连接的事件至。使用合理的超时(例如30秒),以便您可以从轮询呼叫唤醒到扫描并关闭任何似乎没有通过协议链进行的连接。

这基本上要求每个套接字都有自己的“连接”结构,该结构跟踪与您实现的协议相关的连接状态。它可能还意味着保留所有套接字的(哈希)表,以便它们可以映射到它们的连接结构实例。它还意味着“发送”也是非阻塞的。发送和recv无论如何都可以返回部分数据量。

您可以在项目代码here上查看非阻塞套接字服务器的示例。 (在Run方法中查看第360行的主循环开始)。

将套接字设置为非阻塞状态的示例:

int SetNonBlocking(int sock)
{
    int result = -1;
    int flags = 0;

    flags = ::fcntl(sock, F_GETFL, 0);
    if (flags != -1)
    {
        flags |= O_NONBLOCK;
        result = fcntl(sock , F_SETFL , flags);
    }
    return result;
}

答案 3 :(得分:0)

你的代码在成功时会泄漏一个文件句柄,这最终会让你用完fds来分配,使accept()失败。

完成后,

close()套接字。

另外,要直接回答你的问题,除了纠正错误代码之外,没有解决DOS问题的方法。

答案 4 :(得分:0)

我将使用boost::asio功能中的boost::asio::async_connector来创建多个连接处理程序(适用于单线程和多线程环境)。在单线程情况下,您只需要不时运行boost::asio::io_service::run以确保通信有时间处理

你想要使用asio的原因是因为它非常善于处理异步通信逻辑,因此如果连接被阻塞,它就不会阻塞(如你的情况)。您甚至可以安排您希望投入多少处理来打开新连接,同时继续为现有连接提供服务