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なアクセサを経由してアクセス可能な他のクラスも含まれる。これは、注意深いソースコードレビュー以外の方法では発見が難しい厄介な問題であると思う。