7

@ServerEndpoint単純な DDOS 攻撃を防ぐために、単一の IP アドレスからの有効な接続を制限したいJSR-356 を作成しました。

Java ソリューション (JSR-356、Tomcat、または Servlet 3.0 仕様) を検索していることに注意してください。

HandshakeRequestカスタム エンドポイント コンフィギュアラーを試しましたが、オブジェクトでも IP アドレスにアクセスできません。

iptables のような外部ソフトウェアを使用せずに、単一の IP アドレスから JSR-356 接続数を制限するにはどうすればよいですか?

4

4 に答える 4

12

Tomcat 開発者 @mark-thomas によると、クライアント IP はJSR-356 経由で公開されないため、純粋な JSR-356 API でそのような機能を実装することは不可能です。

標準の制限を回避するには、かなり醜いハックを使用する必要があります。

実行する必要があることは次のとおりです。

  1. 各ユーザーに、最初のリクエストで IP を含むトークンを生成します (websocket ハンドシェイクの前)。
  2. エンドポイントの実装に到達するまでトークンをチェーンに渡します

これを実現するには、少なくとも 2 つのハック オプションがあります。

HttpSession を使用する

  1. 着信 HTTP リクエストをリッスンします。ServletRequestListener
  2. 着信要求で呼び出しrequest.getSession()て、セッションがあることを確認し、クライアント IP をセッション属性として保存します。
  3. ServerEndpointConfig.Configuratorメソッドを使用してクライアント IP をリフトし、ユーザー プロパティとしてHandshakeRequest#getHttpSessionアタッチする を作成します。EndpointConfigmodifyHandshake
  4. ユーザー プロパティからクライアント IP を取得し、EndpointConfigそれをマップなどに保存し、IP ごとのセッション数がしきい値を超えた場合にクリーンアップ ロジックをトリガーします。

@WebFilterの代わりにa を使用することもできますServletRequestListener

このオプションは、認証目的などでアプリケーションがすでにセッションを使用していない限り、リソースを大量に消費する可能性があることに注意してください。

URL で IP を暗号化されたトークンとして渡す

  1. Websocket 以外のエントリ ポイントにアタッチするサーブレットまたはフィルターを作成します。例えば/mychat
  2. クライアント IP を取得し、ランダム ソルトと秘密鍵で暗号化してトークンを生成します。
  3. ServletRequest#getRequestDispatcherにリクエストを転送するために使用します/mychat/TOKEN
  4. パスパラメーターを使用するようにエンドポイントを構成します。@ServerEndpoint("/mychat/{token}")
  5. からトークンを持ち上げて@PathParam復号化し、クライアント IP を取得します。マップなどに保存し、IP ごとのセッション数がしきい値を超えた場合にクリーンアップ ロジックをトリガーします。

インストールを簡単にするために、アプリケーションの起動時に暗号化キーを生成することをお勧めします。

クライアントから見えない内部ディスパッチを行っている場合でも、IP を暗号化する必要があることに注意してください。/mychat/2.3.4.5攻撃者が直接接続するのを阻止するものは何もないため、クライアント IP が暗号化されていない場合はスプーフィングされます。

以下も参照してください。

于 2014-04-12T01:37:34.127 に答える
4

ソケット オブジェクトは WsSession に隠されているため、リフレクションを使用して IP アドレスを取得できます。このメソッドの実行時間は約 1ms です。この解決策は完全ではありませんが便利です。

public static InetSocketAddress getRemoteAddress(WsSession session) {
    if(session == null){
        return null;
    }

    Async async = session.getAsyncRemote();
    InetSocketAddress addr = (InetSocketAddress) getFieldInstance(async, 
            "base#sos#socketWrapper#socket#sc#remoteAddress");

    return addr;
}

private static Object getFieldInstance(Object obj, String fieldPath) {
    String fields[] = fieldPath.split("#");
    for(String field : fields) {
        obj = getField(obj, obj.getClass(), field);
        if(obj == null) {
            return null;
        }
    }

    return obj;
}

private static Object getField(Object obj, Class<?> clazz, String fieldName) {
    for(;clazz != Object.class; clazz = clazz.getSuperclass()) {
        try {
            Field field;
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
        }            
    }

    return null;
}

そしてポンの設定は

<dependency>
  <groupId>javax.websocket</groupId>
  <artifactId>javax.websocket-all</artifactId>
  <version>1.1</version>
  <type>pom</type>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-websocket</artifactId>
  <version>8.0.26</version>
  <scope>provided</scope>
</dependency>
于 2015-09-17T11:47:09.327 に答える