Edited at

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

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

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


検証方法

レガシーな技術でつくった、バグだらけのWebアプリケーション「EasyBuggy」のクローンをSpring Bootベースでつくれるか、全てのバグを同様に実装できるどうかで検証しました。


結果(実装の可否)

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


※このアプリは「EasyBuggy Boot」という名前でGitHubに公開しました。詳細については、こちらのページを参照して下さい。

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

それぞれの結果は以下の通りです。特記事項に何も書いていないバグは、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を使うかどうかに関わらず、つくり込むことができます。


解説

少し詳細に説明します。どのような実装になっているかは、実際にソースコードを参照いただいた方がいいかと思います。


デッドロック (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を読むと、エスケープしない旨が記載されていました。

/**

* 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.xmldependency<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のセッション管理機能を使用すると、セッション固定攻撃対策が有効になります。

@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のバグかもしれません(この実装が正しければ)。

@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 を導入していたとしても、以下のように明示的に無効にしていれば、問題になります。

@Configuration

@EnableWebSecurity
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {

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

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


クリックジャッキング

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

@EnableWebSecurity

public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

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


まとめ

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