如何在 C++ 中使用 Berkeley 套接字避免 DOS 攻击
我正在阅读 Richard Stevens 编写的《UNIX 网络编程》第 1 卷,并尝试编写一个使用 Telnet 协议的 TCP Echo 客户端。我仍处于早期阶段并尝试编写读取和写入功能。
我想编写它以使用 I/O 多路复用和 Select 函数,因为它需要多客户端,并且我不想在尝试学习 Berkeley Sockets 库时尝试学习 C++ 线程同时。在 I/O 多路复用这一章的最后,Stevens 有一小节介绍 DOS 攻击,他说我计划使用的方法很容易受到 DOS 攻击,这种攻击只是在连接后发送一个字节,然后挂起。随后他提到了 3 种可能的解决方案 - 非阻塞 IO、线程(out)以及对 I/O 操作设置超时。
我的问题是,还有其他方法可以避免这种攻击吗?如果不是,那么哪个是最好的?我浏览了有关在操作上设置超时的部分,但它看起来不像我想做的事情。他建议的方法看起来相当复杂,我不知道如何将它们应用到我已有的方法中。我只看了一眼关于 NIO 的章节,看起来这就是现在要走的路,但在我再花几个小时仔细阅读这一章之前,我想看看是否还有其他方法可以解决这个问题。
有什么想法吗?
I'm working my way through UNIX Network Programming Volume 1 by Richard Stevens and attempting to write a TCP Echo Client that uses the Telnet protocol. I'm still in the early stages and attempting to write the read and write functions.
I'd like to write it to use I/O Multiplexing and the Select function, because it needs to be multi-client and I don't want to try and tackle learning C++ threads while I'm trying to learn the Berkeley Sockets library at the same time. At the end of the chapter on I/O Multiplexing Stevens has a small section on DOS attacks where he says that the method I was planning on using is vulnerable to DOS attacks that simply send a single byte after connecting and then hang. He mentions 3 possible solutions afterwards - nonblocking IO, threading (out), and placing a timeout on the I/O operations.
My question is, are there any other ways of avoiding such an attack? And if not, which of these is the best? I glanced over the section on placing a timeout on the operations, but it doesn't look like something I want to do. The methods he suggests for doing it look pretty complex and I'm not sure how to work them into what I already have. I've only glanced at the chapter on NIO, it looks like it's the way to go right now, but I'd like to see if there are any other ways around this before I spend another couple of hours plowing through the chapter.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
必读:C10K 问题
每个连接使用线程(或进程)可以编写非常简单的代码。连接数的限制实际上是系统可以轻松执行多任务的线程数的限制。
使用异步 IO 将所有套接字放入单个线程中并不是那么简单的代码(由 libevent 和 很好地包装) libev2),但更具可扩展性 - 它受到系统允许的打开文件句柄数量的限制,并且 - 例如在最近的 Linux 版本上 - 可以以百万计!出于这个原因,大多数 Web 服务器和其他服务器都使用异步 IO。
然而,您的服务器仍然是一种可能会耗尽的有限资源,并且存在比简单地耗尽处理新连接的能力更恶劣的攻击。
防火墙和损坏限制(例如备份、DMZ 等)是真正面向互联网的服务的基本要素。
Essential reading: The C10K Problem
Using threads (or processes) per connection makes for very straightforward code. The limit to the number of connections is really the limit to the number of threads your system can comfortably multi-task.
Using asynchronous IO to put all sockets in a single thread is not such straightforward code (nicely wrapped by libevent and libev2) but is much more scalable - its limited by the number of open file handles your system allows and - on recent linux builds for example - that can be measured in millions! Most web servers and other servers use asynchronous IO for this reason.
However, your server is still a finite resource that can be exhausted, and there are much nastier attacks than simply running out of capacity to handle new connections.
Firewalls and damage limitation e.g. backups, DMZs etc are essential elements in a real internet-facing services.
是的,异步 I/O 是另一种通用方法。
如果问题是阻塞
read()
可能会无限期地挂起您的执行,那么您的一般对策是:多线程、多进程,两者兼而有之。
例如,瞬时(非阻塞 I/O)或非瞬时(
SO_RCVTIMEO
、alarm()
等)例如,
aio_read
对于新手,我建议将非阻塞 I/O 与限时的
select()
/轮询()
。您的应用程序可以跟踪连接是否在“足够短的时间内”生成了“足够的数据”(例如,整行)。这是一种功能强大、可移植且常用的技术。
然而,更好的答案是“这取决于情况”。平台支持,更重要的是,这些选择的设计影响必须根据具体情况进行评估。
Yes, asynchronous I/O is another general approach.
If the problem is that a blocking
read()
may suspend your execution indefinitely, your general countermeasures are then:multi-threaded, multi-process, both.
e.g., instantaneous (non-blocking I/O), or not (
SO_RCVTIMEO
,alarm()
, etc.)e.g.,
aio_read
For the newcomer, I'd suggest non-blocking I/O combined with a time-limited
select()
/poll()
. Your application can keep track of whether or not a connection has generated "enough data" (e.g., an entire line) in a "short enough time."This is a powerful, mostly portable and common technique.
However, the better answer is, "it depends." Platform support and, more importantly, design ramifications from these choices have to be assessed on a case-by-case basis.
如果您刚刚开始学习套接字编程,您可能会更好
专注于套接字的基本功能,目前还不太担心安全问题。当您编写了一些客户端-服务器应用程序并彻底了解它们的工作原理后,您将能够更好地理解它们是如何崩溃的。。
保护面向互联网的网络应用程序免受恶意客户端的侵害绝非易事,可能涉及您提到的所有高级技术,还有一些!
例如,通常将某些责任从应用程序代码转移到路由器或防火墙级别。您可以限制仅访问受信任的主机,或者检测过多的连接尝试,并在流量到达您的应用程序之前限制或阻止它们。
If you're just getting started learning socket programming, you probably would be better off
concentrating on the basic functionality of sockets, and not worrying so much about security issues just yet. When you've written a few client-server applications and understand thoroughly how they work, you'll be in a better position to understand how they break.
Securing an internet-facing network application against malicious clients is not at all trivial, and probably involves all the advanced techniques you mentioned, and then some!
For example, it's common to move some responsibility from the application code to the router or firewall level. You could restrict access to only trusted hosts, or detect excessive connection attempts and throttle or block them before the traffic ever hits your application.
对于服务器,我希望在应用程序级别有一个计时器:
应用程序特定的代码可以终止与已允许空闲“太长时间”的输入缓冲区关联的连接。
这样做意味着异步 I/O,或专用 I/O 线程。
For a server I'd want a timer at the application level:
The application-specific code can terminate the connection associated with input buffers which have been allowed to idle for 'too long'.
Doing this implies asynch I/O, or a dedicated I/O thread[s].
为了解决这个问题,我之前所做的(大约 1997 年:)是要求在一定时间内发送一个幻数,否则连接将关闭。
如果您有异步连接,则套接字不会被阻止,并且您将需要一个线程来轮询尚未发送有效命令的当前连接列表,并且如果在大约 20 毫秒后未收到消息这表示一个有效的命令,然后关闭该连接并执行您需要执行的任何清理操作。
这并不完美,但对于您当前关心的问题,它可能有助于解决它,并允许资源不会因建立太多连接而消耗。
所以它确实需要一个主线程和一个第二线程来进行清理,所以它不是单线程的。
What I have done before, to help with this (circa 1997 :) was to require that a magic number be sent within a certain amount of time else the connection was closed.
If you have an asynchronous connection then the socket won't be blocked, and you would need a thread that can poll through the list of current connections that haven't sent a valid command, and if after about 20ms a message wasn't received that signifies a valid command, then close that connection and do whatever cleanup you need to do.
This isn't perfect, but for your current concern it may help solve it, and allow the resources to not be consumed by making too many connections.
So it does require a main thread and a second thread for cleaning up, so it isn't single-threaded.