かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その10 )( SpotBugs プラグインの findsecbugs-plugin を導入する )

概要

記事一覧はこちらです。

Spring Boot 2.1.x の Web アプリを 2.2.x へバージョンアップする ( その9 )( SpotBugs を 3.1.11 → 4.0.0-beta4 へバージョンアップする ) の続きです。

参照したサイト・書籍

  1. Find Security Bugs
    https://find-sec-bugs.github.io/

目次

  1. build.gradle を変更する
  2. 検知された警告に対応する
    1. 整合性のない暗号(暗号はデータの整合性を提供していません。)
    2. Spring の制限のない RequestMapping による CSRF(Spring の制限のない RequestMapping は,このメソッドを CSRF 攻撃 に対して脆弱にします。)
    3. Information Exposure Through An Error Message(Information Exposure Through An Error Message)
    4. ログの潜在的な CRLF インジェクション(org/slf4j/Logger.info(Ljava/lang/String;)V を使用すると CRLF 文字をログメッセージに含めることができます。)
    5. Freemarker の潜在的なテンプレートインジェクション(Freemarker テンプレートの潜在的なテンプレートインジェクションです。)
    6. ハードコードされたパスワード(ハードコードされたパスワードを発見)

手順

build.gradle を変更する

dependencies {
    ..........

    // for SpotBugs
    compileOnly("com.github.spotbugs:spotbugs:${spotbugsVersion}")
    compileOnly("net.jcip:jcip-annotations:1.0")
    compileOnly("com.github.spotbugs:spotbugs-annotations:${spotbugsVersion}")
    testImplementation("com.google.code.findbugs:jsr305:3.0.2")
    spotbugsStylesheets("com.github.spotbugs:spotbugs:${spotbugsVersion}")
    spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1")
}
  • dependencies block に spotbugsPlugins("com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.1") を追加します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新した後、clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると "BUILD SUCCESSFUL" のメッセージは出力されましたが、The following classes needed for analysis were missing:SpotBugs rule violations were found. See the report at: ... のメッセージも出力されました。

f:id:ksby:20200104231606p:plain f:id:ksby:20200104231710p:plain

The following classes needed for analysis were missing: のメッセージについては Lambda methods reported as missing classes の Issue が上がっていますが、まだ Open の状態でしたので今回は特に何もしません。

SpotBugs rule violations were found. See the report at: ... のメッセージの方はレポートファイルを開くと High Priority Warnings が 27件、Medium Priority Warnings が 4件検知されていました。1つずつ見て対応します。

f:id:ksby:20200104232241p:plain

検知された警告に対応する

整合性のない暗号(暗号はデータの整合性を提供していません。)

レポートファイルに出力されていたのは以下の内容で 1件検知されていました。

f:id:ksby:20200104234057p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Cipher with no integrity のことでした。レポートファイルから Bugs Patterns の該当する Bug Pattern への直接のリンクがないのでちょっと探しずらいですね。

該当箇所 BrowfishUtils.java:[line 38] を見ると以下のソースで、ECB を使用しているために検知されています。

@SuppressWarnings({"PMD.HardCodedCryptoKey"})
public class BrowfishUtils {

    ..........
    private static final String TRANSFORMATION = "Blowfish/ECB/PKCS5Padding";

    public static String encrypt(String str)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException
            , BadPaddingException, IllegalBlockSizeException {
        ..........
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        ..........
    }

}

ここでは ECB に脆弱性があることは知っていて使用しているので、BrowfishUtils クラスに @SuppressFBWarnings("CIPHER_INTEGRITY") を付与して警告が出ないようにします。

@SuppressWarnings({"PMD.HardCodedCryptoKey"})
@SuppressFBWarnings("CIPHER_INTEGRITY")
public class BrowfishUtils {

Spring の制限のない RequestMapping による CSRF(Spring の制限のない RequestMapping は,このメソッドを CSRF 攻撃 に対して脆弱にします。)

レポートファイルに出力されていたのは以下の内容で 26件検知されていました。

f:id:ksby:20200105000755p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Spring CSRF unrestricted RequestMapping のことでした。

これは指摘された通りなので、以下のように対応します。

  • 基本的には @RequestMapping ではなく @GetMapping か @PostMapping に変更します。
  • 以下のメソッドは GET も POST も受け付ける必要があるので、@RequestMapping@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}) に変更して、SpotBugs の検知対象外になるよう @SuppressFBWarnings("SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING") も付与します。
    • ksbysample.webapp.lending.web.LoginController#index メソッド
    • ksbysample.webapp.lending.web.sessionsample.SessionSampleController#index メソッド

Information Exposure Through An Error Message(Information Exposure Through An Error Message)

レポートファイルに出力されていたのは以下の内容で 1件検知されていました。

f:id:ksby:20200105103001p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Information Exposure Through An Error Message のことでした。

該当箇所 ExceptionHandlerAdvice.java:[line 85] を見ると以下のソースでした。

        if (e != null) {
            try (
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw)
            ) {
                e.printStackTrace(pw);  // ← ここ
                ..........

ここはこのままにします。@SuppressFBWarnings("INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE") を付与して SpotBugs に検知されないようにします。

    @SuppressFBWarnings("INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE")
    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception e

ログの潜在的な CRLF インジェクション(org/slf4j/Logger.info(Ljava/lang/String;)V を使用すると CRLF 文字をログメッセージに含めることができます。)

レポートファイルに出力されていたのは以下の内容で 1件検知されていました。

f:id:ksby:20200105104517p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Potential CRLF Injection for logs のことでした。

ログに CRLF が出力されないようにした方が良いらしい。ログは ksbysample.webapp.lending.aspect.logging.RequestAndResponseLogger#logging メソッドで出力しているので、出力時のコードを logger.info(sb.toString()); → `` に変更して CRLF が出力されないようにします。

    private void logging(String title, String name, String value) {
        StringBuilder sb = new StringBuilder(title);
        if (name != null) {
            sb.append(name)
                    .append(" = ");
        }
        sb.append(value);
        logger.info(sb.toString().replaceAll("[\r\n]",""));
    }

Freemarker潜在的なテンプレートインジェクション(Freemarker テンプレートの潜在的なテンプレートインジェクションです。)

レポートファイルに出力されていたのは以下の内容で 1件検知されていました。

f:id:ksby:20200105110427p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Potential template injection with Freemarker のことでした。

エンドユーザーが Freemarker のテンプレートを直接編集できる訳ではないので、ksbysample.webapp.lending.helper.freemarker.FreeMarkerHelper#process メソッドに @SuppressFBWarnings("TEMPLATE_INJECTION_FREEMARKER") を付与して SpotBugs の検知対象外にします。

    @SuppressFBWarnings("TEMPLATE_INJECTION_FREEMARKER")
    private String process(Template template, Map<String, Object> model) {

ハードコードされたパスワード(ハードコードされたパスワードを発見)

レポートファイルに出力されていたのは以下の内容で 1件検知されていました。

f:id:ksby:20200105113005p:plain

findsecbugs-plugin の Bugs Patterns のページで確認したところ Hard coded password のことでした。

該当箇所 WebSecurityConfig.java:[line 163] を見ると以下のソースでした。

    @SuppressWarnings("PMD.SignatureDeclareThrowsException")
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth
            , ApplicationEventPublisher applicationEventPublisher) throws Exception {
        auth.inMemoryAuthentication()
                .withUser(ACTUATOR_USERNAME)
                .password("{noop}xxxxxxxx")  // ←ここ
                .roles("ENDPOINT_ADMIN");
        auth.authenticationEventPublisher(new CustomAuthenticationEventPublisher(applicationEventPublisher))
                .userDetailsService(userDetailsService);
    }

ここは故意に平文でパスワードを書いているので、@SuppressFBWarnings("HARD_CODE_PASSWORD") を付与して SpotBugs の検知対象外にします。

    @SuppressWarnings("PMD.SignatureDeclareThrowsException")
    @SuppressFBWarnings("HARD_CODE_PASSWORD")
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth
            , ApplicationEventPublisher applicationEventPublisher) throws Exception {

以上の対応で SpotBugs で何も検知されなくなりました。

f:id:ksby:20200105115453p:plain

今回導入してみて思いましたが、SpotBugs を入れるなら findsecbugs-plugin も合わせて入れた方がいいですね。

履歴

2020/01/05
初版発行。