如何使用C++中的Berkeley套接字避免DOS攻击

5

我正在阅读Richard Stevens的《UNIX网络编程》第一卷,并尝试编写一个使用Telnet协议的TCP回显客户端。我还处于早期阶段,正在尝试编写读写函数。

我想使用I/O多路复用和Select函数来编写它,因为它需要支持多客户端,而我不想在学习Berkeley套接字库的同时尝试学习C++线程。在I/O多路复用章节的结尾,Stevens有一个关于DOS攻击的小节,他说我计划使用的方法容易受到仅发送一个字节并挂起的DOS攻击。之后,他提到了三种可能的解决方案:非阻塞IO、线程(退出)和在I/O操作上设置超时。

我的问题是,是否有其他避免这种攻击的方法?如果没有,哪种方法最好?我浏览了一下关于在操作上设置超时的章节,但看起来不像我想要的东西。他建议的做法看起来相当复杂,我不确定如何将它们融入到我已经拥有的内容中。我只是浏览了一下NIO章节,它看起来是现在应该采取的方式,但我想看看是否有其他方法可以避免这种情况,然后再花费几个小时浏览该章节。

有什么想法吗?

5个回答

4

必读: C10K问题

使用每个连接的线程(或进程)可以编写非常简单的代码。连接数的限制实际上是您的系统可以轻松多任务处理的线程数的限制。

使用异步IO将所有套接字放在单个线程中并不是那么简单的代码(由libevent和libev2很好地封装),但它更具可扩展性 - 它受到系统允许的打开文件句柄数量的限制,例如,在最近的Linux构建中,这可以以百万计来衡量!出于这个原因,大多数Web服务器和其他服务器都使用异步IO。

然而,您的服务器仍然是有限的资源,可能会被耗尽,并且还有比简单地耗尽处理新连接的能力更糟糕的攻击。

防火墙和损坏限制(例如备份、DMZ等)是真正面向互联网服务的必要元素。


3

...还有其他避免这种攻击的方法吗?

是的,异步I/O是另一种通用方法。

如果问题是阻塞的read()可能会无限期地挂起您的执行,则您的一般对策如下:

  1. 具有多个执行线程

    多线程、多进程、两者皆可。

  2. 限制阻塞操作的时间

    例如瞬间(非阻塞I/O)或不是(SO_RCVTIMEOalarm()等)

  3. 异步操作

    例如aio_read

...其中哪个是最好的?

对于新手,我建议使用非阻塞I/O结合有时间限制的select()/poll()。您的应用程序可以跟踪连接是否在“足够短的时间内”生成了“足够的数据”(例如,整行)。

这是一种强大、大多数情况下可移植且常见的技术。

然而,更好的答案是“这取决于情况”。必须基于具体情况评估平台支持以及更重要的是这些选择带来的设计影响。


2
如果您刚刚开始学习套接字编程,那么最好还是专注于套接字的基本功能,暂时不要过多关注安全问题。当您编写了几个客户端/服务器应用程序并彻底理解它们的工作原理后,您将更能够理解它们的漏洞。
保护面向互联网的网络应用程序免受恶意客户端攻击绝非易事,可能需要您提到的所有高级技术,甚至更多!例如,常见的做法是将一些责任从应用程序代码转移到路由器或防火墙层面。您可以限制访问只针对可信主机,或检测过多的连接尝试并在流量到达应用程序之前进行限制或阻止。

1
我刚开始接触Berkley Sockets的核心。我用Java Sockets编写了一些网络应用程序。我已经使用套接字运行和编写MUD多年了-虽然我使用了一个代码库,并且只是修改了其中的部分,而没有编写套接字部分。我对网络编程的工作原理和理论有很好的理解,只是要学习C / C ++套接字的技巧。此外,我不是在询问所有DOS攻击或所有攻击。只是询问导致阻塞的攻击方法。 - Daniel Bingham

1
我的问题是,是否有其他方法可以避免这种攻击?
对于服务器,我希望在应用程序级别设置一个定时器:
- 每个连接都有一个输入数据缓冲区 - 简单的套接字读取代码将数据从套接字读入输入缓冲区 - 应用程序特定的代码解析输入缓冲区的内容
应用程序特定的代码可以终止与已经允许空闲时间过长的输入缓冲区相关联的连接。
这样做意味着异步I/O,或者专用的I/O线程。

1

为了解决这个问题(大约在1997年),我以前所做的是要求在一定时间内发送一个魔术数字,否则连接将被关闭。

如果您有一个异步连接,那么套接字不会被阻塞,您需要一个线程来轮询当前连接列表中尚未发送有效命令的连接,并且如果在大约20毫秒后没有收到表示有效命令的消息,则关闭该连接并执行所需的清理操作。

这并不完美,但对于您目前的问题可能有所帮助,可以避免通过建立过多的连接消耗资源。

因此,它需要一个主线程和一个用于清理的第二个线程,因此它不是单线程的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接