Wicket×Dropwizard で構築された自作アプリケーションに対して脆弱性を検証してみる


Django製の Mezzanine で構築されたブログに対して、脆弱性を検証してみる | Monotalk
で、OWASP ZAP を使用して実施した検証を、作成中のアプリケーションに対して実施し、
警告に対して対処していこうと思います。


前提

Wicket × Dropwizard でアプリケーションは構築されています。
まだ日の目を見ておらず、localhost での確認となります。

環境情報

  • OS Version

    sw_vers    
    ----------------------------
    ProductName:    Mac OS X
    ProductVersion: 10.11.6
    BuildVersion:   15G1108
    ----------------------------
    

  • Wicket version 7.6.0です。

        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-core</artifactId>
            <version>7.6.0</version>
        </dependency>
    

  • Dropwizard version 1.0.6です。

        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-core</artifactId>
            <version>1.0.6</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-servlets</artifactId>
            <version>1.0.6</version>
        </dependency>
        <dependency>
            <groupId>io.dropwizard</groupId>
            <artifactId>dropwizard-assets</artifactId>
            <version>1.0.6</version>
        </dependency>
    

検証の内容

  • OWASP ZAP は デフォルト設定で、プロキシ設定なしの、標準モードで実行
    インストールして起動すると表示されるクイックスタートに、
    localhost のアプリケーションの URL を入力するだけです。
    OWASP ZAP クイックスタート

  • 検証対象の URL プロトコルは HTTP
    localhost での検証となり、HTTPS での検証はできておりません。

  • TOP 画面に入力項目の有無 入力項目はありません

検証結果に対しての対応方針

  • 脆弱性が見つかり、Wicket でも、Dropwizard でも対応できる場合は、Wicket側で対応する。
    Dropwizard が、HTTP サーバ× AP サーバのレイヤとなるため、本来 Dropwizard 側での対応が良い気はします。
    個人的な興味から、Wicket 側に実装します。

アプリケーションのTOP URL を指定した検証結果

以下の警告が出力されました。

  • X-Frame-Options ヘッダの欠如

  • WebブラウザのXSS防止機能が有効になっていません。

  • X-Content-Type-Optionsヘッダの設定ミス

  • Cross-Domain JavaScript Source File Inclusion

  • Cookie No HttpOnly Flag


対応方法

参考

wicket の user guide の セキュリティ項が参考になりました。1 [1] web 検索ではなぜか上手く hit せず、github の adoc への直リンクとなります。

Wicket で、HttpResponseHeader を設定する

これで以下のヘッダに対する対応が可能です。

  • X-Frame-Options ヘッダの欠如

  • Web ブラウザの XSS 防止機能が有効になっていません。

  • X-Content-Type-Options ヘッダの設定ミス

wicket の user guide に記載されている通り、RequestCycleListener を作成して、
response header の設定処理を実装しました。

  • HttpHeaderAddingRequestCycleListener.java

    import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
    import org.apache.wicket.request.cycle.RequestCycle;
    import org.apache.wicket.request.http.WebResponse;
    
    /**
     * HttpHeaderAddingRequestCycleListener
     */
    public class HttpHeaderAddingRequestCycleListener extends AbstractRequestCycleListener {
    
        private boolean isDeployment;
    
        /**
         * Construct.
         *
         * @param isDeployment
         */
        public HttpHeaderAddingRequestCycleListener(boolean isDeployment) {
            this.isDeployment = isDeployment;
        }
    
        @Override
        public void onEndRequest(RequestCycle cycle) {
            WebResponse response = (WebResponse) cycle.getResponse();
            response.setHeader("X-XSS-Protection", "1; mode=block");
            response.setHeader("X-Content-Type-Options", "nosniff");
            response.setHeader("X-Frame-Options", "sameorigin");
    
            // In the deployment mode, the Https related header is set
            if (isDeployment) {
                response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
                response.setHeader("Content-Security-Policy", "default-src https:");
            }
        }
    }
    

  • 説明 以下のHeaderについては、localhostで、http通信時は問題がありますので、
    Deploymentモードの場合のみ、出力するようにしました。

    response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
    response.setHeader("Content-Security-Policy", "default-src https:");
    

Applicationクラスでは以下のように呼び出しています。

getRequestCycleListeners().add(new HttpHeaderAddingRequestCycleListener(usesDeploymentConfig()));

Dropwizard で、HttpResponseHeader を設定する

  • X-Content-Type-Options ヘッダの設定ミス

は静的コンテンツ類でも発生しています。
静的コンテンツ類のマウントは、Dropwizard の AssetBundle で実施していて、
このリソースは、Wicket のRequestCycle の制御対象外になります。
こちらは別途 CustomBundle クラスを作成して、Filterでresponse header を設定するようにしました。
以下、記事の実装を参考にさせてもらいました。

import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.setup.Environment;
import org.eclipse.jetty.servlet.FilterHolder;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.EnumSet;

/**
 * NoSniffAssetBundle
 */
public class NoSniffAssetBundle extends AssetsBundle {

    public NoSniffAssetBundle() {
        this("/assets", "/assets", "index.htm", "assets");
    }

    public NoSniffAssetBundle(String path) {
        this(path, path, "index.htm", "assets");
    }

    public NoSniffAssetBundle(String resourcePath, String uriPath) {
        this(resourcePath, uriPath, "index.htm", "assets");
    }

    public NoSniffAssetBundle(String resourcePath, String uriPath, String indexFile) {
        this(resourcePath, uriPath, indexFile, "assets");
    }

    public NoSniffAssetBundle(String resourcePath, String uriPath, String indexFile, String assetsName) {
        super(resourcePath, uriPath, indexFile, assetsName);
    }

    public void run(Environment environment) {
        super.run(environment);
        environment.getApplicationContext()
                .addFilter(newNoSniffResponseFilterHolder(),
                        getUriPath(),
                        EnumSet.of(DispatcherType.ASYNC,
                                DispatcherType.REQUEST,
                                DispatcherType.FORWARD,
                                DispatcherType.INCLUDE,
                                DispatcherType.ERROR));
    }

    private FilterHolder newNoSniffResponseFilterHolder() {
        FilterHolder holder = new FilterHolder();
        holder.setName("NoSniffResponseFilter");
        holder.setFilter(new Filter() {

            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
                //Do Nothing...
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                httpResponse.setHeader("X-Content-Type-Options", "nosniff");
                chain.doFilter(request, response);
            }

            @Override
            public void destroy() {
                //Do Nothing...
            }
        });
        return holder;
    }
}

Cross-Domain JavaScript Source File Inclusion

外部サイトの javascript 読み込みを行っていると、発生する警告です。
こちらの警告発生は許容しました。

これは、jsessionid が HttpOnly になっていないため、出力されていました。
Dropwizard (というかJetty) の SessionManager の実装クラスのsetHttpOnly()
設定が可能です。

以下の通り、true を設定しました。

sessionManager.setHttpOnly(true);
env.servlets().setSessionHandler(new SessionHandler(sessionManager));

上記対処で、警告は、

  • Cross-Domain JavaScript Source File Inclusion

のみになりました。
以上です。

コメント

カテゴリー