脆弱性対策方法

開発者の不注意や確認不足が、脆弱性につながることが理解できたと思います。
次は、サンプルコードを基に対策方法を学んでいきましょう。

脆弱性の原因

本シナリオでの脆弱性の原因は以下となります。
  • 独自のTrustManagerや HostnameVerifierを使用している
    69~70行目に該当する部分です。
    独自のTrustManagerを使用している事が原因ですので、初期設定でのTrustManagerを使用することで対策します。
  • SSLExceptionを適切に処理していない
    83行目に該当する部分です。
    SSLExceptionに対して適切な処理がなされていないので例外処理を追加する事が対策となります。

対策方法の概要

実際に脆弱性のあるソースコードを確認し、修正してみましょう。
まず、「ソースコードを開く」ボタンをクリックし、「MainActivity.java」を開いてください。

ソースコードを開く

修正前:
プロジェクト SSLConnection
ソースファイル MainActivity.java
    try {

        //Private証明書をassetsから読み込み、検証
        KeyStore ks = KeyStoreUtil.getEmptyKeyStore();
        KeyStoreUtil.loadX509Certificate(ks,
                mContext.getResources().getAssets().open("server.crt"));

        // ▼▼▼脆弱性のあるソースコード▼▼▼
        // 1.独自でTrustManagerを実装したクラス(PrivateSSLSocketFactory)を実装
        // 2.HostnameVerifierにSSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIERをセット
        SSLSocketFactory sslSocketFactory = new PrivateSSLSocketFactory(ks);
        sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        Scheme sch = new Scheme("https", sslSocketFactory, 443);
        // ▲▲▲脆弱性のあるソースコード▲▲▲

        client.getConnectionManager().getSchemeRegistry().register(sch);

        HttpGet request = new HttpGet(params[0]);

        HttpResponse response = client.execute(request);

        webView.loadData(EntityUtils.toString(response.getEntity()), "text/html", "UTF-8");
    // ▼▼▼脆弱性のあるソースコード▼▼▼
    } catch (Exception e) {

    }
    // ▲▲▲脆弱性のあるソースコード▲▲▲
    return null;
}

このアプリには脆弱性対策を行うべき箇所が2箇所あります。修正例は以下の通りです。
  • ライブラリのインポート
    この修正を行うには、下記のライブラリをインポートする必要があります。
    修正中のMainActivity.javaの先頭にあるimport文の下に、以下のimport文を追加してください。
    • import javax.net.ssl.SSLException;
  • 69~71行目
    独自のTrustManagerとHostnameVerifierの使用を止め、標準のTrustManagerを使用するよう修正します。
    このアプリで使用している独自のTrustManagerはSSLExceptionを無視するよう設計されているため、中間者攻撃を受けた場合にSSLExceptionを処理することができません。
    標準のTrustManagerを使用することにより、SSLExceptionを処理できるようになり、中間者攻撃を受けた際のエラー処理を行うことができるようになります。
    69~70行目を削除し、71行目を以下のように修正します(赤文字部分)。
    Scheme sch = new Scheme("https", new SSLSocketFactory(ks), 443);
    
  • 81~82行目
    SSLExceptionのcatchブロックを追加します。
    71行目で標準のTrustManagerを使用するよう修正したことにより、SSLExceptionを処理できるようになります。
    中間者攻撃を受けた際に発生するSSLExceptionを処理することにより、通信の中止や利用者への通知など、エラー処理を行えるようになります。
    今回はサンプルアプリのため、処理をキャンセルするよう例外処理を実装していますが、開発するアプリに合わせ適切な例外処理を行うようにしましょう。
    81行目と82行目の間に以下の処理を追加します。
    }catch (SSLException e){
        this.cancel(true);
    

修正後:
プロジェクト SSLConnection
ソースファイル MainActivity.java
    try {

        //Private証明書をassetsから読み込み、検証
        KeyStore ks = KeyStoreUtil.getEmptyKeyStore();
        KeyStoreUtil.loadX509Certificate(ks,
                mContext.getResources().getAssets().open("server.crt"));

        // ▼▼▼脆弱性を修正したソースコード▼▼▼
        Scheme sch = new Scheme("https", new SSLSocketFactory(ks), 443);
        // ▲▲▲脆弱性を修正したソースコード▲▲▲

        client.getConnectionManager().getSchemeRegistry().register(sch);

        HttpGet request = new HttpGet(params[0]);

        HttpResponse response = client.execute(request);

        webView.loadData(EntityUtils.toString(response.getEntity()), "text/html", "UTF-8");
    // ▼▼▼脆弱性を修正したソースコード▼▼▼
    } catch (SSLException e) {
        this.cancel(true);
    // ▲▲▲脆弱性を修正したソースコード▲▲▲
    } catch (Exception e) {
    }
    return null;
}

対策のまとめ1

「独自のTrustManagerや HostnameVerifierを使用している」による脆弱性について、対策は以下のとおりです。
  • 対策
    標準のTrustManagerや HostnameVerifierを使用する
  • 修正方法
    独自のTrustManagerや HostnameVerifierを使用しないようにします。

対策のまとめ2

SSLExceptionを適切に処理していない」による脆弱性について、対策は以下のとおりです。
  • 対策
    SSLExceptionを適切に処理する
  • 修正方法
    catchブロックでアプリの仕様に応じた例外処理を実行します。