1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RemoteIpFilter と RemoteIpValve の違いってなんだ?

Posted at

Tomcat 9.0.76 (2023-06-09) および 10.1.9 (2023-05-19) にて、RateLimitFilter というフィルタが追加されていました。このフィルタにより DoS 攻撃やブルートフォース攻撃を軽減できるとされています:

Add: Add RateLimitFilter which can be used to mitigate DoS and Brute Force attacks. (isapir)

ここで RateLimitFilter のリファレンスを参照すると、以下のような記載があります:

WARNING: if Tomcat is behind a reverse proxy then you must make sure that the Rate Limit Filter sees the client IP address, so if for example you are using the Remote IP Filter, then the filter mapping for the Rate Limit Filter must come after the mapping of the Remote IP Filter to ensure that each request has its IP address resolved before the Rate Limit Filter is applied. Failure to do so will count requests from different IPs in the same bucket and will result in a self inflicted DoS attack.

日本語訳:

警告: Tomcat がリバースプロキシの後ろにある場合、Rate Limit Filter がクライアント IP アドレスを見れていることを確認する必要があります。例えば Remote IP Filter を使用している場合、Rate Limit Filter のフィルタマッピングは Remote IP Filter のマッピングの後に行われ、Rate Limit Filter が適用される前に各リクエストの IP アドレスが解決されていることを確認する必要があります。これを怠ると、異なる IP からのリクエストを同じバケットでカウントすることになり、自作自演の DoS 攻撃を受けることになります。

「自作自演の DoS 攻撃」とは中々怖いことが書かれていますね。RemoteIpFilter を使用している場合は設定状況を確認した方が良さそうです。

「自作自演の DoS 攻撃」を発生させてみる

「自作自演の DoS 攻撃」は RateLimitFilter が作動する段階で、プロキシサーバーやロードバランサのアドレスからクライアントのアドレスに置換されていないことで発生します。url-pattern が同じフィルタの適用順序は filter-mapping が定義されている順番に依存します。そのため、RemoteIpFilterfilter-mappingRateLimitFilter より上方で定義する必要があることになります:

web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
  metadata-complete="true">

    <display-name>自作自演にならない例</display-name>

    <filter>
        <filter-name>RemoteIpFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
    </filter>

    <filter>
        <filter-name>RateLimitFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RateLimitFilter</filter-class>
        <init-param>
            <param-name>bucketRequests</param-name>
            <param-value>5</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>RemoteIpFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>RateLimitFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
  metadata-complete="true">

    <display-name>自作自演になる例</display-name>

    <filter>
        <filter-name>RemoteIpFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
    </filter>

    <filter>
        <filter-name>RateLimitFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RateLimitFilter</filter-class>
        <init-param>
            <param-name>bucketRequests</param-name>
            <param-value>5</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>RateLimitFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>RemoteIpFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

RateLimitFilter は「自作自演にならない例」ではクライアント IP アドレスを見てくれますが、「自作自演になる例」ではプロキシサーバーやロードバランサの IP アドレスを見てしまします。

なお RateLimitFilter によって遮断されると、クライアントには HTTP Status 429 – Too Many Requests が返却され、ログに下記のような警告が出力されます:

25-Sep-2023 13:34:56.279 WARNING [http-nio-8080-exec-6] org.apache.catalina.filters.RateLimitFilter.doFilter [RateLimitFilter] [6] Requests from [172.17.0.2] have exceeded the maximum allowed of [5] in a [65] second window.

RemoteIpFilter と RemoteIpValve

ということで RemoteIpFilter について情報収集をしていたのですが、その中で RemoteIpValve という別機能を解説している記事に出会いました。あまりにも名前が類似していますが、どのような違いがあるのでしょうか。それぞれのドキュメントを読み比べてみましょう。なお、文中の mod_remoteip とは Apache HTTPd 用モジュールの一種を指し、プロキシやロードバランサが HTTP ヘッダに付加した情報でクライアント IP アドレスを置換します。

RemoteIpValve:

Tomcat port of mod_remoteip, this valve replaces the apparent client remote IP address and hostname for the request with the IP address list presented by a proxy or a load balancer via a request headers (e.g. "X-Forwarded-For").

Another feature of this valve is to replace the apparent scheme (http/https) and server port with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").

RemoteIpFilter:

Servlet filter to integrate "X-Forwarded-For" and "X-Forwarded-Proto" HTTP headers.

Most of the design of this Servlet Filter is a port of mod_remoteip, this servlet filter replaces the apparent client remote IP address and hostname for the request with the IP address list presented by a proxy or a load balancer via a request headers (e.g. "X-Forwarded-For").

Another feature of this servlet filter is to replace the apparent scheme (http/https) and server port with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").

やはり似たようなことが出来そうですが、RemoteIpFilter からは Servlet filter の一種であることが読み取れます。その名の通り Filter はサーブレット仕様の一部であり、Java EE / Jakarta EE で定義されています。そのため、Tomcat に限らず Jetty や Glassfish など他のサーブレットコンテナでも使用可能です。

一方、Tomcat Valve は Tomcat に固有の機能です。Filter はサーブレット単位で動作しますが、Valve は Catalina container レベルで動作します。たとえば、Tomcat のアクセスログ設定は Access Log Valve で設定しますが、この Valve はどのサーブレットに来たリクエストもロギングできます。

Valve は Filter より処理されるタイミングが早いです。つまり、RemoteIpValve を使用する場合 RateLimitFilter との食い合わせを気にする必要はありません。RateLimitFilter が作動する段階でクライアントのアドレスに置換されているためです。実際、以下の設定を反映してみると「自作自演にならない例」と同様の挙動になることがわかります:

web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0"
  metadata-complete="true">

    <display-name>RemoteIpFilter を定義しない</display-name>

    <filter>
        <filter-name>RateLimitFilter</filter-name>
        <filter-class>org.apache.catalina.filters.RateLimitFilter</filter-class>
        <init-param>
            <param-name>bucketRequests</param-name>
            <param-value>5</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>RateLimitFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
server.xml
<Valve className="org.apache.catalina.valves.RemoteIpValve" />

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?