4

次のような C/C++ で書かれたコードがあります。

    while(1)
{
    //Accept
    struct sockaddr_in client_addr;
    int client_fd = this->w_accept(&client_addr);
    char client_ip[64];
    int client_port = ntohs(client_addr.sin_port);
    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));

    //Listen first string
    char firststring[512];
    memset(firststring,0,512);
    if(this->recvtimeout(client_fd,firststring,sizeof(firststring),u->timeoutlogin) < 0){
        close(client_fd);
    }

    if(strcmp(firststring,"firststr")!=0)
    {
        cout << "Disconnected!" << endl;
        close(client_fd);
        continue;
    }

    //Send OK first string
    send(client_fd, "OK", 2, 0);


    //Listen second string
    char secondstring[512];
    memset(secondstring,0,512);
    if(this->recvtimeout(client_fd,secondstring,sizeof(secondstring),u->timeoutlogin) < 0){
        close(client_fd);
    }

    if(strcmp(secondstring,"secondstr")!=0)
    {
        cout << "Disconnected!!!" << endl;
        close(client_fd);
        continue;
    }

    //Send OK second string
    send(client_fd, "OK", 2, 0);


}
    }

だから、それは処分可能です。サーバーを停止する非常に単純な dos スクリプトを perl で作成しました。

#Evildos.pl
use strict;
use Socket;
use IO::Handle;
sub dosfunction
{
my $host = shift || '192.168.4.21';
my $port = 1234;
my $firststr = 'firststr';
my $secondstr = 'secondstr';
my $protocol = getprotobyname('tcp');
$host = inet_aton($host) or die "$host: unknown host";
socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";
my $dest_addr = sockaddr_in($port,$host);
connect(SOCK,$dest_addr) or die "connect() failed: $!";
SOCK->autoflush(1);
print SOCK $firststr;
#sleep(1);
print SOCK $secondstr;
#sleep(1);
close SOCK;
}

my $i;
for($i=0; $i<30;$i++)
{
&dosfunction;
}

30回ループでサーバーダウン。

問題は、この種の攻撃を回避できる方法、システム、ソリューションがあるかどうかです。

編集: recvtimeout

int recvtimeout(int s, char *buf, int len, int timeout)
     {


fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
    return -2; // timeout!
}
if (n == -1){
    return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
    }
4

5 に答える 5

4

一般に、DOS 攻撃に対する 100% 効果的なソフトウェア ソリューションはないと思います。あなたが何をしようとも、誰かがあなたのネットワーク インターフェイスで処理できるよりも多くのパケットをスローする可能性があります。

ただし、この特定のケースでは、プログラムは一度に 1 つの接続しか処理できないようです。つまり、着信接続 #2 は、接続 #1 がトランザクションを完了する (またはタイムアウトになる) まで処理されません。つまり、これは明らかな難点です。攻撃者がしなければならないことは、サーバーに接続してから何もしないことだけであり、サーバーは事実上無効になります (タイムアウト期間の長さに関係なく)。

これを回避するには、一度に複数の TCP 接続を処理するようにサーバー コードを書き直す必要があります。非ブロッキング I/O に切り替える (O_NONBLOCK フラグを fcntl() に渡す) か、select() や poll() などを使用して一度に複数のソケットで I/O を待機するか、複数のスレッドまたはサブプロセスを生成して、着信接続を並行して処理するか、非同期 I/O を使用します。(個人的には最初の解決策を好みますが、すべてがさまざまな程度で機能します)。最初のアプローチでは、特定の IP アドレスからの新しいソケットを受け入れる前に、特定の IP アドレスから既存のソケットを強制的に閉じるなどのことを行うことも実用的です。一度にサーバー、

同時に多数の TCP 接続を処理する方法について詳しくは、この記事をお読みください。

于 2012-05-28T15:46:38.850 に答える
2

DOS および DDOS 攻撃の主な問題は、ユーザーの弱点を利用することです。つまり、サービスを提供するために使用できるメモリ/ポート数/処理リソースが限られているという事実です。Amazon ファームのようなものを使用して無限のスケーラビリティ (または近いスケーラビリティ) を持っている場合でも、請求額が屋根を通り抜けるのを避けるために、おそらくそれを制限したいと思うでしょう。

サーバー レベルでは、主な心配事は、自己保存制限を課すことによってクラッシュを回避することです。たとえば、処理できることがわかっている接続の最大数を設定し、他の接続を単に拒否することができます。

完全な戦略には、ファイアウォールなどの特殊な素材が含まれますが、それらをプレイする方法は常にあり、それと一緒に暮らす必要があります.

厄介な攻撃の例として、ウィキペディアでスローロリスについて読んでください。

Slowloris は、ターゲット Web サーバーへの多くの接続を開いたままにし、できるだけ長く開いたままにしようとします。これは、ターゲット Web サーバーへの接続を開き、部分的な要求を送信することによって実現されます。定期的に後続の HTTP ヘッダーを送信し、リクエストに追加しますが、完了することはありません。影響を受けるサーバーは、これらの接続を開いたままにし、最大同時接続プールをいっぱいにして、最終的にクライアントからの追加の接続試行を拒否します。

DOS 攻撃にはさまざまなバリエーションがあるため、特定の答えを出すのは非常に困難です。

于 2012-05-28T15:43:33.067 に答える
1

これは DOS 攻撃の万能薬ではありませんが、ノンブロッキング ソケットを使用するとスケーラビリティに確実に役立ちます。また、スケールアップできれば、多くの DOS 攻撃を軽減できます。この設計変更には、受け入れ呼び出しで使用されるリッスン ソケットとクライアント接続ソケットの両方を非ブロッキングに設定することが含まれます。

次に、recv()、send()、または accept() 呼び出しでブロックする代わりに、poll、epoll、または select 呼び出しのいずれかでブロックし、その接続のイベントを可能な限り処理します。適切なタイムアウト (30 秒など) を使用して、ポーリング呼び出しから目を覚まし、プロトコル チェーンを介して進行していないように見える接続をスイープして閉じることができるようにします。

これには基本的に、実装するプロトコルに関してその接続の状態を追跡する独自の「接続」構造体がすべてのソケットに必要です。また、すべてのソケットの (ハッシュ) テーブルを保持して、接続構造インスタンスにマップできるようにすることも意味します。また、「送信」も非ブロッキングであることを意味します。いずれにせよ、send と recv は部分的なデータ量を返すことができます。

私のプロジェクト コードで非ブロッキング ソケット サーバーの例をここで見ることができます。(Run メソッドのメイン ループの開始については、360 行目あたりを参照してください)。

ソケットを非ブロッキング状態に設定する例:

int SetNonBlocking(int sock)
{
    int result = -1;
    int flags = 0;

    flags = ::fcntl(sock, F_GETFL, 0);
    if (flags != -1)
    {
        flags |= O_NONBLOCK;
        result = fcntl(sock , F_SETFL , flags);
    }
    return result;
}
于 2012-05-28T16:50:55.087 に答える
0

boost::asio::async_connector複数の接続ハンドラを作成するには、boost::asio機能を使用します (シングル スレッド環境とマルチスレッド環境の両方で動作します)。シングルスレッドの場合、boost::asio::io_service::run通信が処理される時間を確保するために時々実行する必要があります

asio を使用する理由は、非同期通信ロジックの処理が非常に優れているため、接続がブロックされても (あなたの場合のように) ブロックされないためです。既存の接続を維持しながら、新しい接続を開くためにどれだけの処理を行うかを調整することもできます

于 2012-05-28T15:51:55.290 に答える
0

コードが成功するとファイルハンドルがリークします。これにより、最終的には割り当てる fds が不足し、accept()失敗します。

close()使い終わったらソケット。

また、あなたの質問に直接答えるために、それを修正する以外に、欠陥のあるコードによって引き起こされた DOS の解決策はありません。

于 2012-05-28T15:49:05.270 に答える