ユーザーがランダムなIDをループしてアプリケーションIDを推測しようとしているWebサービスに問題があります。
悪いリクエストはランダムなIPから来ているので、私はそれらのIPを禁止することはできません(動的にそれを行わない限り、私はまだそれを調べていません)。
現在、不正なアプリIDを10回試行したクライアントを検出すると、それらをアプリのブロックリストに追加し、そのIPからのそれ以上のリクエストをその日に拒否します。
悪いクライアントは拒否されても何千ものリクエストを送信し続けるので、サーバーが実行する必要のある作業の量を最小限に抑えたいと思います。動的ファイアウォールソリューションがあることは知っていますが、今のところ、アプリに簡単に実装できるものが必要です。現在、通話を減らすために5秒間スリープしていますが、クライアントに応答を送信しないだけなので、タイムアウトする必要があります。
Java、JAX-RSでこれを行う方法を知っている人はいますか?
私のサービスは、
@Path("/api")
public class MyServer {
@GET
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
@Path("/my-request")
public String myRequest(String type,
@Context HttpServletRequest requestContext,
@Context HttpServletResponse response) {
...
}
JAX-RSでサポートされている 非同期応答 を探しています。 Jerseyのチュートリアル には、リクエストへの非同期応答を実装する方法の例がいくつか含まれています。
非同期応答では、要求への応答を担当するスレッドは、要求が完了する前にすでに別の要求を処理するために解放されます。この機能は、@Suspended
アノテーション付きのパラメーターを追加することでアクティブになります。さらに行う必要があるのは、この例のように、特定のタイムアウト後にリクエストをウェイクアップする専用の スケジューラー を登録することです。
@Path("/api")
public class MyServer {
private ScheduledExecutorService scheduler = ...;
@GET
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
@Path("/my-request")
public String myRequest(String type,
@Context HttpServletRequest requestContext,
@Context HttpServletResponse response,
@Suspended AsyncResponse asyncResponse) {
scheduler.schedule(new Runnable() {
@Override
public void run() {
asyncResponse.resume(...)
}
}, 5, TimeUnit.SECOND);
}
}
このように、5秒間の待機時間の間、スレッドがブロックされることはありません。これにより、その間に他の要求を処理する機会が与えられます。
JAX-RSは、応答なしで要求を完全に破棄する方法を提供していません。タイムアウトを生成するには、接続を開いたままにしておく必要があります。接続を終了すると、ユーザーに終了が通知されます。最善の方法は、非同期リクエストに応答しないことですが、それでも一部のリソースを消費します。これを回避したい場合は、JAX-RSの外部で問題を解決する必要があります。たとえば、別のサーバーでリクエストをプロキシするなどです。
これを行う1つの方法は、 set upmod_proxy で、悪意のあるリクエストのエラーコードでプロキシに応答できます。そして、そのようなリクエストに対して非常に大きな再試行制限を設定します。
考えられる解決策はたくさんありますが、あなたが与えた制限により、2つの可能な解決策が思い浮かびます。
1)リクエストの制限をすでにサポートしているフォワードプロキシを使用します。私は個人的に Nginx を使用しましたが、セットアップが簡単なこともあり、お勧めできます。関連するレート制限設定: Limit Req Module
2) Asynchronous JAX-RS を使用して、検出した悪意のあるリクエストをタイムアウトさせます。正常な要求は直接処理できます。ただし、そのようなアプローチではサーバー上のリソースが消費されるため、結果に注意してください。
IP拒否ロジックをRESTからプレーンHTTPフィルターに移動することをお勧めします:
@WebFilter(urlPatterns = "/*", asyncSupported = true)
@WebListener
public class HttpFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if(denyIP(servletRequest.getRemoteAddr())) {
AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.setTimeout(100000);
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() { }
private boolean denyIP(String address){
//todo
return true;
}
}
アプリケーションサーバーの方が安価です。XML/ JSONデシリアライズなし、RESTクラスへの呼び出しなし。また、asyncContext.start
を呼び出さないことに気付くかもしれません。Wildfly8.2サーバーをチェックします。これでケースWildflyはリクエストごとにスレッドを使用しません。多くのリクエストを送信しましたが、スレッドの量は一定でした。
PS
ランダムなIDをループしてアプリケーションIDを推測しようとしています
DOS攻撃ではありません。ブルートフォース攻撃です。
Tomcat3.0でサポートされているasyncContextを試すことができます。この機能は、Web要求ハンドラーとプロセッサーを分離します。あなたの場合、リクエストを受け入れるスレッドは、設定されたタイムアウトよりも長く待機/スリープする必要があります。同じスレッドを長時間スリープ状態にすると、それらが不足し、サーバーのパフォーマンスに大きな影響を与えます。したがって、非同期処理が正しい方法です。
Javaシングルスレッドエグゼキュータ http://docs.Oracle.com/javase/7/docs/api/Java/util/concurrent)でasyncContextを使用しました/ThreadPoolExecutor.html それは私にとってはうまくいきました。私は同様のビジネスケースを持っていて、アプリケーションをモックする必要がありました。
実装についてはこれを参照してください http://peter-braun.org/2013/04/asynchronous-processing-from-servlet-3-0-to-jax-rs-2-0/
シングルスレッドエグゼキュータはリソースを消費せず、このユースケースに最適です。
私の知る限り、サーブレット仕様にはそのようなメカニズムはありません。 JAX-RS実装はサーブレット(少なくともJerseyとResteasy)を使用するため、Javaでそれを実現する標準的な方法はありません。
非同期JAX-RSを使用するという考えは、Thread.sleep(5000)
よりも優れていますが、それでもいくつかのリソースを使用し、常に要求を無視するのではなく、後で要求を処理する方法にすぎません。
私はかつてTCP/IPトンネルアプリケーションを作成することで同様の問題を解決しました。
これは、外部ポート(httpポート80など)をリッスンする小さなアプリケーションです。通常の状態では、受信したすべての呼び出しが受け入れられ、専用のソケットオブジェクトが作成されます。 これらの個々のソケットは、同じ物理サーバーまたはローカルネットワーク内の任意のサーバーで実行される実際の(非表示の)Webサーバーを呼び出します。
トンネル自体はディスパッチャに似ていますが、ロードバランサーのように機能することもできます。 )。多機能にすることができます。
問題は、この時点でソケットを使用して低レベルで作業しているということです。禁止されているIPアドレスのリストをこのアプリケーションに渡すと、非常に低レベルのアプリケーションをシャットアウトできます(アプリケーションを受け入れない場合もあります)ソケット呼び出し)。
これをまったく同じJavaアプリケーションに統合することもできます。しかし、これを別のアプリケーションと見なす方が柔軟だと思います。
(ソースコードが必要な場合はお知らせください。開始するためにコードを配置している場合があります)
私はこれを試したことがありません....ここでは暗闇の中でのショットだけなので、一粒の塩でそれを取ります。したがって、問題は、何か怪しいものを検出してIPをブロックモードにすると、このリクエストで別のリソースを無駄にしたり、タイムアウトの時間を無駄にしたりしたくないということです。ただし、例外をスローすると、フレームワークは応答します。現在のスレッドを中断するのはどうですか?これはThread.currentThread().interrupt();
で実行できます。 Javaコンテナがリクエストを処理すると、割り込みステータスがチェックされることが期待されます。それが行われていない可能性があります。IO関連クラスは見たことがありませんが、割り込みフラグが設定されたため、要求を処理します。
編集:スレッドの割り込みが機能しない場合は、InterruptedExceptionをスローすることもできます。それは望ましい効果を達成するかもしれません。