WebアプリケーションとリフレクションAPI

StrutsのRequestProcessorは、BeanUtils.populate(Object bean, Map properties)を使用して、リクエストパラメータをActionFormへ格納する。
BeanUtilsは、リフレクションAPIを簡易に扱うためのラッパーであり、上記メソッドはMapのキーに対応するbeanのプロパティに対してMapのvalueを格納するという動作をする(beanのプロパティへのアクセサはpublicである必要がある)。

Mapのキーに.を含めることで、ネストしたプロパティにアクセスすることも可能であり、リクエストパラメータが aaa.bbb=ccc である場合、 bean.getAaa().setBbb(ccc) という展開が実行されることになる。

ネストしたプロパティが扱えることは非常に便利ではあるが、その反面、見えにくい危険性があるということも認識しておかなかればならない。

public class FooForm extends ActionForm {
    private BarBean bean = new BarBean();
    
    public BarBean getBarBean() {
        return bean;
    }

    public void setBarBean(BarBean bean) {
        this.bean = bean;
    }
}

public class BarBean extends XXX {
    //外部から操作されてもかまわないフィールド
    private String baz;
    
    public String getBaz() {
        return baz;
    }
    public void setBaz(String baz) {
        this.baz = baz;
    }
}

例えば、上記のActionFormは、一見何も問題がないコードのように思える。
しかし、基底クラスが以下のようなメソッドを保持していた場合、このコードは危険なコードに変わることになる。

public class XXX {
    //外部から操作されては困るフィールド
    private String yyy;
    
    public void setYyy(String yyy) {
        this.yyy = yyy;
    }
}

この挙動は、言われてみると当然だと感じられるが、実装の盲点となりやすいポイントであると思う。実際、私がこれまで作成してきたアプリケーションは、なるべくセキュアになるよう心がけてきたつもりだが、この挙動をついた攻撃に耐性があるよう作られているかと言われると自信がない。
Struts本家でも、ActionFormのpublicメソッドである getMultipartRequestHandler() を悪用した攻撃として、以下の問題が報告されていた。
http://issues.apache.org/bugzilla/show_bug.cgi?id=38534

問題は、ネストしたプロパティとしてアクセス可能な全てのクラスと、そのメソッドについて安全性を確認しなければならないことだ。全てのクラスにはObjectクラスや、Classクラスなども含まれる。そこからpublicなアクセサを経由してアクセス可能な他のクラスも含まれる。これは、注意深いソースコードレビュー以外の方法では発見が難しい厄介な問題であると思う。

コード解析

最近、コード解析ツールにものすごく興味が湧いてきた。
今までは、どうせ誤検知ばっかで重大な問題は見つけられなかったりすんじゃない?と否定的だったのだけれど、そうでもないかもしれなくない?という感覚がふつふつと沸いてきた感じ。今の仕事に飽きてきて、新しいことがしたくなっただけかもしれないけれど。
バイトコード解析とソースコード解析を組み合わせると結構なところまで辿り着ける予感がするので、本腰を入れて頑張ってみよう。3年以内にはなんらかの形にまとめたい。

Struts XSS Vulnerability

IPAに報告した脆弱性情報が先日公開されました。
http://jvn.jp/jp/JVN%2316535199/index.html

結果としてはTomcat脆弱性であると位置付けられてしまったのですが、Struts脆弱性として報告をあげていた問題です。
概要は、Strutsが提供するHTML用タグライブラリがAccept-Languageヘッダ値をエスケープせずに出力するためXSSが発動してしまう、というものです。
攻撃にはFlashが必要となるため、「Flash脆弱性である」という回答が来る可能性もあるかな?と考えていたのですが、「Tomcatの問題だ!」という予想外の回答がIPA経由で返ってきました。どうやら「Tomcatなどのアプリケーションコンテナは、RFC2616に従ってAccept-Languageヘッダ値をvalidateすべきである、validateされていれば、XSSは発動しない。」という開発者の主張があったようです。
ん〜なんだかなぁ・・・
「Cross-Site Scriptingの問題は、特別な理由がない限りHTMLを生成する処理系が解決すべきであると考えています。そうでないと、複合的な条件で発生する脆弱性が闇に埋もれてしまう可能性が高くなりますよ。」
といったニュアンスの返信を開発者に伝えてもらったのですが、返事がもらえなかったようです。しばらくして、Tomcat脆弱性として公開してはどうか?というIPAからの提案がありました。この時点で報告から1年近く経過していたこともあり、闇に埋もれさせるよりはその形の方がよいと判断して、公開してもらうことにしました。


本当は以下のような形での公開を望んでいたのですけれど・・・

概要

The Apache Software Foundation により提供されている Web アプリケーションフレーム ワーク Apache Struts には、Accept-Language ヘッダの処理に関するクロスサイトスクリプティング脆弱性が存在します。

詳細情報

Strutsが提供するhml:htmlタグは以下のようなHTML生成ロジックを持ちます。

このlang属性の出力値には、Accept-Languageヘッダ値を元に決定されるServletRequest.getLocale()メソッドの戻り値が使用されるため、ServletAPIの実装によっては、クロスサイトスクリプティング脆弱性が発生する可能性があります。
報告者の情報によると、本脆弱性は、最新バージョンの Flash を使用している場合にも起こる問題であると確認されています。

影響を受けるシステム

Apache Struts 1.3.5及びそれ以前

想定される影響

ユーザのブラウザ上で任意のスクリプトが実行される可能性があります。

対策方法

・アップデートする
Apache Struts 1.3.6以上にアップデートしてください。

脆弱性を持つタグを使用しない
html:htmlタグのlang属性にtrueを指定しているアプリケーションにおいて、問題が発生する可能性があります。バージョンアップが難しい場合でも、lang属性を指定しなければ、問題を回避することが可能です。

CookieMonster

今まで見逃してたけど、CookieMonsterの対策方法はRFC2965に書いてあった。
http://www.ietf.org/rfc/rfc2965.txt

7.2  Cookie Spoofing

   Proper application design can avoid spoofing attacks from related
   domains.  Consider:

      1. User agent makes request to victim.cracker.edu, gets back
         cookie session_id="1234" and sets the default domain
         victim.cracker.edu.

      2. User agent makes request to spoof.cracker.edu, gets back cookie
         session-id="1111", with Domain=".cracker.edu".

      3. User agent makes request to victim.cracker.edu again, and
         passes

         Cookie: $Version="1"; session_id="1234",
                 $Version="1"; session_id="1111"; $Domain=".cracker.edu"

         The server at victim.cracker.edu should detect that the second
         cookie was not one it originated by noticing that the Domain
         attribute is not for itself and ignore it.

自分が発行したCookieのDomain属性は自分で認識出来るので、怪しげなCookieが送られてきたらはじくことが出来ますね。
ブラウザはSet-Cookie2を実装すべきだと思うんだけど、今のところ対応しているのはOperaぐらい?