Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

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

検証方法

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

結果(実装の可否)

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

migration.png

※このアプリは「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やフレームワークを使うことで、つくり込みを防止できる問題はあります。しかし、それはごく一部であり、プログラマーはこれらの問題について理解し、正しい実装をする必要があるといえます。

tamura__246
OSSエンスー(OpenAMコミッタ)
https://t246osslab.wordpress.com/
nri
NRIは「コンサルティング」「金融 ITソリューション」「産業 ITソリューション」「IT 基盤サービス」の4事業でお客様のビジネスや快適な社会、暮らしを支えています。※各記事の内容は個人の見解であり、所属する組織の公式見解ではありません。
https://www.nri.com/jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした