总共600分-完成端口 的高手来讨论,如何对付只连接不传输数据的[拒绝服务]攻击? (200分)

  • 主题发起人 主题发起人 delphiroad
  • 开始时间 开始时间
D

delphiroad

Unregistered / Unconfirmed
GUEST, unregistred user!
使用完成端口实现了一个服务器程序,每成功建立一个连接,服务
器就必须分配一些内存。正常断开了再释放这些资源。
在我的监听线程中,如果有连接则得到一个Accept的socket:
Accept = WSAAccept(Listen,NULL,NULL,NULL,0);
然后马上在这个socket上投寄一个接收操作(就是马上就要求客户端上传数据):
WSARecv(Accept......
该操作马上完成,但接收还在等待。
现在的问题是,如果客户端不上传数据,我也没有办法断开这个Accept的连接。
假如,存心攻击我的人,建立了大量无用的连接(尽管不做传递任何数据),长时间
不断开,不久我的服务器就会资源耗尽!于是拒绝接受其它正常的连接请求。
我做了一点改进:
接收到非法的数据(比如信息头不正确等)则丢弃;
但这基本上对攻击者没什么意义,因为他不会发任何信息给我(以免发错了给我断开
了),也就是不给我断开连接的机会。
想过的第一个办法是:同一个IP只允许建立一个连接。
但是,第一是如何设计一个算法很快的查出这个Ip在之前已经建立一个连接了(因为
服务器被设计成可接收数以万计合法连接的,采用完成端口本身就是为了达到大响应)?
第二是好像用Ip判断也不可靠(如果Ip欺骗呢?尽管不容易)。
曾想过的第二个办法是:是否存在一种事件(超时事件)通知的模式
于是想到对每个建立起来的socket使用WSAEventSelect投寄事件,然后用在另一个线程
中用WSAWaitForMultipleEvents来等待事件发生,并且设一个超时,如果超时了,就代
表WSARecv超时了。
但WSAWaitForMultipleEvents一次只能等待60个socket的事件,如果多了我不是要建立
很多个线程来等待,这不单是线程多的关系,还有就是WSAWaitForMultipleEvents返回
的是事件对象在数组中的相对索引(而不是socket),那我就得自己去对应到socket对象,
还是在好几个数组(每个数组是最多60个事件Handle)中进行对应,这也加大了服务器的
负荷。
第三个办法是用另一个线程来隔一段时间就用getsockopt来判断每个socket连接有多长
时间没有动作了,但是用轮询的办法对成千上万的socket来判断开销也是可观的!
如果做这个事情需要系统付出很大的开销则是得不偿失的,因为攻击者同样可以利用这
一点来达到让服务器拒绝正常服务的目的。
在网上还看到原来拒绝服务攻击是很普遍的(并且象这种只建立连接不传递数据的攻击方
法又很简单,几乎不需要面对什么技术难题),同时还知道有分布式的拒绝服务攻击DDoS,
那就意味者攻击者可以利用互联网上所有的计算机来攻击我!就算我可以做到让一个Ip
只能建立一个连接,也没有办法阻止它啊!
有没有什么办法既可自动断开长时间不作为的连接同时开销又很小呢?

这帖子是200分,还有以前两个帖子(一直没有答案,现在自己已经知道了),如果回答得
精彩的,到时散分时,请“得奖”者到这两个帖子跟贴领分:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1700719
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2239070
 
完成端口函数里有个参数好象是定义收发是否超过时间用的,忘记是哪个了,你找找看吧。
 
实在找不着!
 
应该在client端程序中判断,一段时间过后没有访问服务器,就断开。
 
没看清楚问题,我说的是,如果客户端是恶意连接,它还会给你断开吗?
 
那个函数是GetQueuedCompletionStatus,好象是最后一个参数表示是否超过指定的收发时间就断开连接。过两天回公司偶再查查。
 
找不着ux
 
GetQueuedCompletionStatus设的超时仅仅是GetQueuedCompletionStatus的超时,
我要的是WSARevc的超时(虽然WSARecv立即返回了,但其实接收还没有结束,并且
可能是无休止的等待下去!)。我是在Accept之后立即在相应的socket上投寄一个
WSARevc的!
 
可以有3种方式:
1、使用alarm函数
2、使用select阻塞时间设置
3、使用setsockopt设置socket选项
  SO_RCVTIMEO接收超时时间设置
  SO_SNDTIMEO发送超时时间设置
unix下,windows下一样
 
chyun:
你说的第三个办法如果可行那应该是最佳办法了,我暂时试不了(要去公司试),你
能把你 的三个办法写几句简单的代码演示一下吗?
如果真是这么简单那就好了。
当然大家也可以谈一下其它拒绝服务攻击的应付方式。
 
可以再服务器端建立一个监听客户端连接的信息,对恶意多个连接无数据通讯的采用间接断开(可限制同一IP最大的连接数量),不过这样做会浪费些系统资源
 
设置时间限制,对连接无数据通讯的在超过设置间间后断开。
如果真有人对你ddos,你是没有办法的,你只有丢弃来源于他的所有数据包。
 
借宝贴移用,求教一个Winsock问题:

http://www.delphibbs.com/delphibbs/dispq.asp?lid=2423292
 
请大家不要只是把思路说出来!编写服务器,效率要高、系统开销要低就已经是一个思路了,
其实象netwatch(不好意思,在这里点名只是为了说明问题,完全没有不尊重的意思,还是要感
谢netwatch跟贴的!)提出来的思路应该是我们大家都想过的思路,因此重要的已经不是思路了,
而是怎么去实现?又能达到目的,同时开销最小!
bluearc:
你的“在服务器端建立一个监听客户端连接的信息”这句话我不太明白,建立一个信息,建立什
么信息?如何建立?我第一次接触这种思路?能否更具体的说一说?
 
下面的例子都是在2秒后超时,这里只是简单的写了一下,没有做出错判断等。
例子1:使用alarm
void function(int signo)
{
return;
}
signal(SIGALRM, function);
alarm(2);
.....
alarm(0);

例子2:使用select
int sock;
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(sock, &rset);
tv.tv_sec = 2;
tv.tv_usec = 0;
select(sock+1, &rset,NULL, NULL, &tv);

例子3:使用setsockopt(这个好象在windows下只有winsock2才支持)
int sock;
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
chyun:
你的三个办法我都试过了(我的环境是win2000+VC++6):
1、signal、SIGALRM、alarm都是未声明的undeclared identifier!
不知道你说的是不是Unix操作系统下的
2和3、都只是在将socket设成阻塞的情况下有效!
我用的是“完成端口”模型,WSARevc调用之后立即返回,剩下的由GetQueuedCompletionStatus
去等候。
就是因为这样子才难阿!
至少我敢肯定setsockopt+SO_RCVTIMEO必须在socket阻塞的情况下才行。
哎!真想去问问M$到底这个问题有没有救!
 
可能没救了!我断开再快也不及他很多台机连接来得快,况且我对每个连接还有个延迟(超时),我有点后悔提这个问题了。
!!!!难道就没有办法吗!!!!!!!!!!!!!!!!
 
你不会弄个黑名单之类的东西,在Accept处理中。
如果是黑名单的IP,你就直接closesocket。
黑名单怎么来的,就像Serv-U一样的那个东西,做个东西:N时间内连接次数不能在M范围内,超过,就加入黑名单;而且限制IP数量,超过,也加入黑名单

而N,M怎么控制,我觉得就在Accept后,IOCP不是会GetMemory给一个accept socket吗?那里加入一个时间域,然后找到这个IP上次连接时间,看看相差多少。
 
procedure TForm1.Button1Click(Sender: TObject);
begin
with SQLQuery1 do
begin
close;
sql.Text :=' select * into #mytemp from sales';
execsql;
end;
end;
可以多次建立临时表,SQL服务器不用重起。
D7+WinXP+SQL2000个人版+dbExperss
运行进程->按BUTTON1->退出->再运行进程->按BUTTON1->退出
 
accept 一个socket后,如果觉得找这个socket IP的时间过长,可以用上下文关联。
即IP是固定的,一个IP连接上来,为它建立一个数据内容,它的生存期跟你的进程一样。这样,只是在accept处理时,才需要去查找这个内容,关联后,以后就直接使用了。

我也没做过,呵,说说而已
 
后退
顶部