简体   繁体   English

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

[英]How to Avoid DOS Attack using Berkeley Sockets in C++

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. 我正在通过Richard Stevens的UNIX网络编程第1卷工作,并尝试编写使用Telnet协议的TCP Echo客户端。 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. 我想把它写成使用I / O Multiplexing和Select函数,因为它需要是多客户端的,我不想尝试学习C ++线程,而我正在尝试学习Berkeley套接字库同时。 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. 在关于I / O多路复用的章节结束时,史蒂文斯有一小部分关于DOS攻击,他说我计划使用的方法容易受到DOS攻击的影响,只需在连接后发送一个字节然后挂起。 He mentions 3 possible solutions afterwards - nonblocking IO, threading (out), and placing a timeout on the I/O operations. 之后他提到了3种可能的解决方案 - 非阻塞IO,线程化(输出)以及在I / O操作上设置超时。

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. 我只看了一眼有关NIO的章节,它看起来就像是现在的方式,但是我想看看是否还有其他方法可以解决这个问题,然后再花几个小时来讨论这一章。

Any ideas? 有任何想法吗?

Essential reading: The C10K Problem 基本阅读: C10K问题

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! 使用异步IO将所有套接字放在一个线程中并不是那么简单的代码(很好地由libevent和libev2包装 )但是更具可伸缩性 - 它受到系统允许的打开文件句柄数量的限制 - 例如在最近的Linux构建中 - 可以用数百万来衡量! Most web servers and other servers use asynchronous IO for this reason. 因此,大多数Web服务器和其他服务器都使用异步IO。

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 eg backups, DMZs etc are essential elements in a real internet-facing services. 防火墙和损坏限制,例如备份,DMZ等,是真正面向互联网的服务中的基本要素。

... are there any other ways of avoiding such an attack? ......还有其他方法可以避免这种攻击吗?

Yes, asynchronous I/O is another general approach. 是的, 异步I / O是另一种通用方法。

If the problem is that a blocking read() may suspend your execution indefinitely, your general countermeasures are then: 如果问题是阻塞read()可能会无限期地暂停执行,那么您的一般对策就是:

  1. Have multiple threads of execution 有多个执行线程

    multi-threaded, multi-process, both. 多线程,多进程,两者兼而有之。

  2. Time-limit the blocking operation 限制阻塞操作的时间限制

    eg, instantaneous (non-blocking I/O), or not ( SO_RCVTIMEO , alarm() , etc.) 例如,瞬时(非阻塞I / O)或不( SO_RCVTIMEOalarm()等)

  3. Operate asynchronously 异步操作

    eg, aio_read 例如, aio_read

... which of these is the best? ......哪些是最好的?

For the newcomer, I'd suggest non-blocking I/O combined with a time-limited select() / poll() . 对于新手,我建议使用非阻塞I / O和限时select() / poll() Your application can keep track of whether or not a connection has generated "enough data" (eg, 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! 保护面向Internet的网络应用程序免受恶意客户端攻击并非易事,可能涉及您提到的所有高级技术,然后是一些! 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. 您可以限制只能访问受信任的主机,或检测过多的连接尝试,并在流量到达您的应用程序之前限制或阻止它们。

My question is, are there any other ways of avoiding such an attack? 我的问题是,还有其他方法可以避免这种攻击吗?

For a server I'd want a timer at the application level: 对于服务器,我想要一个应用程序级别的计时器:

  • Input data buffer per connection 每个连接输入数据缓冲区
  • Dumb socket-reading code reads data from the socket into the input buffer 哑套接字读取代码将数据从套接字读入输入缓冲区
  • Application-specific code parses the content of the input buffer 特定于应用程序的代码解析输入缓冲区的内容

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]. 这样做意味着异步I / O或专用I / O线程[s]。

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. 我以前做过的,为了帮助这个(大约1997年:)是要求在连接关闭的一定时间内发送一个幻数。

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. 如果你有一个异步连接,那么套接字将不会被阻塞,你需要一个可以通过当前连接列表轮询尚未发送有效命令的线程,如果在大约20ms后没有收到消息表示有效命令,然后关闭该连接并执行您需要执行的任何清理操作。

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. 因此它需要一个主线程和一个第二个线程来清理,所以它不是单线程的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM