How to avoid a DOS attack using Berkeley Sockets in C ++

I work through UNIX Network Programming Volume 1 by Richard Stevens and am trying to write a TCP Echo Client that uses the Telnet protocol. I am still at an early stage and am trying to write read and write functions.

I would like to write it to use I / O multiplexing and a selection function, because it must be a multi-client, and I don't want to try to learn C ++ topics while I try 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 planned to use is vulnerable to DOS attacks that simply send one byte after connecting and then hang. He then mentions 3 possible solutions: a non-blocking IO, flows (out), and putting a timeout in an I / O operation.

My question is, are there other ways to avoid such an attack? And if not, which one is better? I looked through the section on how to set a timeout for operations, but this does not look like what I want to do. The methods that he offers for this look rather complicated, and I'm not sure how to use them in what I already have. I just looked at the NIO chapter, it looks like it is now, but I would like to see if there are other ways to get around this before I spend a couple more hours wading through the chapter.

Any ideas?

+4
source share
5 answers

... are there other ways to avoid such an attack?

Yes, asynchronous I / O is another common approach.

If the problem is that a read() lock can pause your execution indefinitely, your general countermeasures will be as follows:

  • Has multiple threads

    multi-threaded, multi-processor, both.

  • Lock time limit

    e.g. instantaneous (non-blocking I / O) or not ( SO_RCVTIMEO , alarm() , etc.)

  • Work asynchronously

    e.g. aio_read

... which one is better?

For beginners, I offer non-blocking I / O in combination with a limited time of select() / poll() . Your application can track whether a connection has generated “enough data” (for example, a whole line) in a “short time”.

This is a powerful, mostly portable and common method.

However, the best answer is: "It depends." Platform support and, more importantly, the estimated consequences of these choices should be evaluated on a case-by-case basis.

+3
source

Basic reading: Problem C10K

Using threads (or processes) for each connection makes very simple code. The limit on the number of connections is actually the limit on the number of threads that your system can comfortably multitask.

Using asynchronous I / O to place all sockets in one stream is not so simple code (beautifully wrapped libevent and libev2 ), but much more scalable - it is limited by the number of open files your system processes and, for example, on the latest versions of linux, which measure in millions! For this reason, most web servers and other servers use asynchronous I / O.

However, your server is still the end resource that may be exhausted, and there are many unpleasant attacks than just a lack of capacity to handle new connections.

Firewalls and damage control, for example. backups, dmz, etc. are important elements in real internet services.

+4
source

If you are just starting to learn socket programming, you will probably be better off concentrating on the basic functionality of sockets and not worrying about security issues. When you have written several client-server applications and carefully studied how they work, you can better understand how they break.

Protecting your Internet application from malicious Internet applications is not at all trivial and probably includes all the best practices that you mentioned, and then some! For example, to transfer some responsibility from the application code to the level of a router or firewall. You can restrict access only to trusted hosts or detect excessive connection attempts and block or block them before traffic ever gets into your application.

+2
source

My question is, are there other ways to avoid such an attack?

For the server, I need a timer at the application level:

  • Input buffer for each connection
  • Dumb socket read code reads data from the socket into the input buffer
  • Application-specific code parses the contents of the input buffer

Application code may terminate the connection associated with input buffers that are allowed to remain idle for too long.

This implies asynch I / O or dedicated I / O stream [s].

+1
source

What I did before to help with this (around 1997 :) was to require the magic number to be sent within a certain time, otherwise the connection was closed.

If you have an asynchronous connection, the socket will not be blocked, and you need a stream that can poll the list of current connections that did not send a valid command, and if after 20 ms there was no message, the value received means a valid command, then close this connection and make all you have to do.

This is not ideal, but for your current problem, it can help solve the problem and allow resources not to be consumed, creating too many connections.

Therefore, cleaning requires a main stream and a second stream, so it is not single-threaded.

+1
source

All Articles