Spring Bootベースのアプリケーションに脆弱性やリソースリークをつくり込めるか

**最新のフレームワークであるSpring Bootをベースにしたアプリケーションにも、脆弱性やリソースリークをつくり込むことはできるでしょうか？また、どのような実装をすると問題となるのでしょうか？**

実際に検証してみました。

## 検証方法

レガシーな技術でつくった、バグだらけのWebアプリケーション[「EasyBuggy」](https://github.com/k-tamura/easybuggy/blob/master/README.jp.md)のクローンをSpring Bootベースでつくれるか、全てのバグを同様に実装できるどうかで検証しました。

## 結果(実装の可否)

結果から言うと、Spring Bootベースで開発しても、全てのバグをつくり込むことができました。

![migration.png](https://qiita-image-store.s3.amazonaws.com/0/43869/b59567b1-918f-e092-f391-00dbe2d758e2.png)

<table width="100%"><tr><td>※このアプリは[「EasyBuggy Boot」](https://github.com/k-tamura/easybuggy4sb/blob/master/README.jp.md)という名前でGitHubに公開しました。詳細については、[こちらのページ](http://qiita.com/tamura__246/items/c8229d68ef199890827b)を参照して下さい。</td></tr><table>

ほとんどのバグはつくり込むことが難しくはありませんでしたが、一般的な構成では実現できないものや実装しづらいもの、少しつくりを変えて実装しなければならないものもありました。

それぞれの結果は以下の通りです。特記事項に何も書いていないバグは、Javaのロジック上の問題のため、Spring Bootベースで開発するかどうかに関わらず、つくり込むことができます。「可否」が「△」となっているものは、非推奨の技術や古いバージョンや使用することで実現したものなので、通常のSpring Bootの構成ではつくり込む可能性が低いと考えられます。

|バグ|可否|特記事項|
--------|--------|--------
|デッドロック (Java)|○||
|デッドロック (SQL)|○|明示的にトランザクション管理することで実現。|
|完了しないプロセスの待機|○||
|無限ループ|○||
|メモリリーク (Javaヒープ領域)|○||
|メモリリーク (パーマネント領域/MetaSpace)|○||
|メモリリーク (Cヒープ領域)|○||
|ネットワークソケットリーク|○||
|データベースコネクションリーク |○|Spring JDBCにはプールからコネクションを取得するメソッドがあるので、それを利用。|
|ファイルディスクリプタリーク |○||
|スレッドリーク|○||
|文字化け|○|デフォルトで、文字コードに関する煩わしい問題が発生しないような仕組みになっている。そのため、文字化けは発生しにくいが、文字コードを変更する方法は提供されている。|
|整数オーバーフロー|○||
|丸め誤差|○||
|打ち切り誤差|○||
|情報落ち|○||
|XSS (クロスサイトスクリプティング)|○|Thymeleafを使っていれば、XSSは簡単に実現可能。|
|SQLインジェクション|○|Spring JDBCを使っていても、SQLの文字列にユーザーの入力値を連結すれば実現できる。|
|LDAPインジェクション|○|Spring LDAPのメソッドが、LDAPインジェクションの対象となる文字をエスケープしていなかったので、それを利用。|
|コードインジェクション|○||
|OSコマンドインジェクション|○||
|メールヘッダーインジェクション|△|古いバージョンのJavaMailでSpring Mailをオーバーライドして実現。|
|Nullバイトインジェクション|△|バージョン1.7.0_40より前のJavaを使用することで実現可能。|
|サイズ制限の無いファイルアップロード|△|application.propertiesで明示的にサイズ制限を無効(またはそれと同等に大きな値)にできる。|
|拡張子制限の無いファイルアップロード|○||
|オープンリダイレクト可能なログイン画面|○||
|ブルートフォース攻撃可能なログイン画面|○||
|セッション固定攻撃可能なログイン画面|○|Spring Scurityのセッション固定攻撃の抑止機能を正しく実装しなければ、実現可能。|
|親切過ぎる認証エラーメッセージ|○||
|危険なファイルインクルード|△|ThymeleafではなくをJSPを使用することで実現。|
|パストラバーサル|△|ThymeleafではなくをJSPを使用することで実現。|
|意図しないファイル公開|△|ディレクリリスティングを有効にすれば可能。|
|CSRF (クロスサイトリクエストフォージェリ)|○|Spring ScurityのCSRF攻撃の抑止機能を正しく実装しなければ、実現可能。|
|クリックジャッキング|○|Spring Scurityのクリックジャッキングの抑止機能を正しく実装しなければ、実現可能。|
|XEE (XMLエンティティ拡張)|○||
|XXE (XML外部エンティティ)|○||
|正規表現解析による遅延|○||
|プラス演算子による文字列結合の遅延|○||
|不必要なオブジェクト生成による遅延|○||

EasyBuggyでは、`NullPointerException`などの例外や`OutOfMemoryError`などのエラーもワンクリックで発生するように実装していますが、それらについても、Javaのロジック上の問題のため、Spring Bootを使うかどうかに関わらず、つくり込むことができます。

## 解説

少し詳細に説明します。どのような実装になっているかは、実際に[ソースコード](https://github.com/k-tamura/easybuggy4sb)を参照いただいた方がいいかと思います。

### デッドロック (SQL)

`PlatformTransactionManager`かアノテーション`@Transactional`で、明示的にトランザクションを管理するように実装し、複数スレッドが逆順でレコードを更新すれば、デッドロックを実現できます。

### データベースコネクションリーク

Spring JDBCを使う場合、基本的に直接コネクションプールからコネクションを取得する必要は無いと思います。ただし、それができなくなったわけではく、コネクションを取得するメソッドも存在するので、そのメソッドを利用してコネクションリークをつくり込むことは可能です。

### 文字化け

意外と実装しづらかったのが文字化けの問題です。サーブレットフィルターで文字コードを適当なものに変更すれば、文字化けすると思っていたのですが、実際にやってみると文字化けしませんでした。`OrderedCharacterEncodingFilter`を継承し、`super.setEncoding("[適当な文字コード]");`することで文字コードをUTF-8以外に変更でき、文字化けを発生させるようにしました。

### XSS (クロスサイトスクリプティング)

Thymeleaf の`th:utext`を使用すれば、サニタイズ処理は行われないので、XSSは簡単に実装できます。

### SQLインジェクション

SQLの文字列は連結してつくれるので、Spring JDBCを使っても、SQLインジェクションの回避は不可能です。

### LDAPインジェクション

Spring LDAPを使いましたが、LDAPフィルターの文字列をエスケープしていないメソッドがありました。エスケープ処理の実装漏れのバグかと思ったのですが、`LdapQuery.filter(String)`のJavadocを読むと、エスケープしない旨が記載されていました。

```java
/**
 * Specify a hardcoded filter. Please note that using this method, the filter string will not be
 * validated or escaped in any way. <b>Never</b> use direct user input and use it concatenating strings
 * to use as LDAP filters. Doing so opens up for &quot;LDAP injection&quot;, where malicious user
 * may inject specifically constructed data to form filters at their convenience. When user input is used
 * consider using {@link #where(String)} or {@link #filter(String, Object...)} instead.
 *
 * @param hardcodedFilter The hardcoded filter string to use in the search.
 * @return this instance.
 * @throws IllegalStateException if a filter has already been specified.
 */
public LdapQuery filter(String hardcodedFilter) {
    initRootContainer();
    rootContainer.append(new HardcodedFilter(hardcodedFilter));
    return this;
}
```

### メールヘッダーインジェクション

`pom.xml`の`dependency`の`<artifactId>javax.mail</artifactId>version>1.5.1</version>`で下位のバージョンに上書きしなければ実現できなかったので、最新のSpring Bootを利用している場合は、この脆弱性を気にしなくてもいいかと思います。

### Nullバイトインジェクション

バージョン1.7.0_40より前のJavaの脆弱性なので、Spring Bootの推奨する前提バージョン1.8では再現できません。ただし、Spring Bootはバージョン1.7のJavaでも動かすことができるので、その場合は再現が可能です。

### サイズ制限の無いファイルアップロード

`application.properties`に明示的に`spring.http.multipart.max-file-size=-1`を定義することで再現できます。

### セッション固定攻撃

このアプリではSpring Scurityは実装しておらず、対策もしていません。以下のようにSpring Scurityのセッション管理機能を使用すると、セッション固定攻撃対策が有効になります。

```java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig
    extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.sessionManagement().sessionFixation().migrateSession();
    }
}
```

なお、この例では、`.sessionFixation().migrateSession()`を呼び出しているので、ログイン前に使用していたセッションを破棄し、新たにセッションを生成します。

### 危険なファイルインクルード

Spring Bootでは非推奨となっているJSP/JSTLを使うことで実現しました。ThymeleafにはJSTLの`c:import`の代替が無いので、リモートファイルインクルードが実現できませんが、`th:include`をすることでローカルファイルインクルードは実現できます。

### パストラバーサル

Spring Bootでは非推奨となっているJSP/JSTLを使うことで実現しました。Thymeleafを使用する場合は、「`../`」でディレクトリをさかのぼれないため、パストラバーサルが実現できませんでした。

### 意図しないファイル公開

次のような実装で実現しましたが、これを行うと、ThymeleafやJSPを使用した動的なページ生成ができなくなります。同じバージョンの非Spring Boot組み込みのTomcatのディレクリリスティングとは動作が異なっているので、Spring Bootのバグかもしれません(この実装が正しければ)。

```java
@Bean
public ServletRegistrationBean servletRegistrationBean() {
    /* Enable directory listing under /uid/ */
	final DefaultServlet servlet = new DefaultServlet();
	final ServletRegistrationBean bean = new ServletRegistrationBean(servlet, "/uid/*");
	bean.setEnabled(true);
	bean.addInitParameter("listings", "true");
	bean.setLoadOnStartup(1);
	return bean;
}
```

### CSRF (クロスサイトリクエストフォージェリ)

CSRF攻撃の抑止機能はSpring Security 4.0からデフォルトで有効となっています。Spring Security を導入していたとしても、以下のように明示的に無効にしていれば、問題になります。

```java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig
    extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();
    }
}
```

### クリックジャッキング

Spring Scurityにクリックジャッキングの抑止機能がありますが、正しく実装しなければ脆弱性をつくり込むことになります。正しい実装例は以下のようになります。

```java
@EnableWebSecurity
public class WebSecurityConfig extends
	WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.headers().frameOptions().sameOrigin();
	}
}
```

## まとめ

以上の通り、最新のJavaやフレームワークを使うことで、つくり込みを防止できる問題はあります。しかし、それはごく一部であり、プログラマーはこれらの問題について理解し、正しい実装をする必要があるといえます。