いきなり本題ですが、
サーブレットのURLのパス中にセミコロンでつなげられた文字列は、サーブレットの呼び出しについては無視されますが、HttpServletRequest#getRequestURI()の戻り値には含まれます。
どうゆうことかというと、以下のようなサーブレットにアクセスするためのURLがあったとします。
http://www.acchi.jp/kocchi/OneServlet?hoge=moge
これは以下のようにアクセスしても正常に呼び出されます。
http://www.acchi.jp/kocchi/OneServlet;(-_-;)?hoge=moge
このときgetRequestURI()の戻り値には、セミコロンで追加された文字列も含まれます。
/kocchi/OneServlet;(-_-;)
セッション管理が必要な携帯サイトをサーブレットで構築した方ならご存知と思いますが、URLリライティングによりURLのパスの後ろ、パラメータの前にセミコロンでつなげてセッションIDが埋め込まれます。
http://www.acchi.jp/kocchi/OneServlet;jsessionid=...?hoge=moge
過去に、同様に別のパラメータを追加できないか試したのと、そのアプリで独自のアクセスログを実装、URLの取得に最初getRequestURI()を使っていたことで、経験的にこのことを知っていました。
これは何か?
RFC3986 - Uniform Resource Identifier (URI): Generic Syntax の3.3.Pathによると、パスの後ろに追加するのではなく、
パスセグメント(上の例のパス/kocchi/OneServletでいえば、kocchiと OneServlet)に付加情報を追加するための機能のようです。確かにTomcatをインストールした直後のExamplesで試してみると、サンプルのサーブレット、
http://localhost:8080/examples/servlets/servlet/HelloWorldExample
は以下のようにしても正常に呼び出されます。
http://localhost:8080/examples;orz/servlets;o.rz/servlet;o..rz/HelloWorldExample;o...rz
一方、
海外のサイトではこのような追加パラメータをPath Parameterと呼び、ファイル名の後ろにつけるもの、と説明しているものもあります。日本語で「パスパラメータ」で検索しても、それらしいものは見つかりません。
何が問題か?
開発者が意図しない文字列をパス中に挿入できるわけですから、 もし開発者が「
正常にサーブレットが呼び出されたのだから、getRequestURI()の戻り値に不正な文字が含まれていることはない」という前提でコーディングをしていれば、Webアプリの脆弱性につながる場合もあるでしょう。そのままDBに保存すれば
SQLインジェクション、そのまま画面上に表示すれば
XSSされる可能性があります。
特にサーブレットの場合はweb.xmlファイルの中で<url-pattern>を指定するので、正常に呼び出されたサーブレットやJSPの パス中に、SQL文やJavaScriptが埋め込まれるはずなどないと思いがちでしょう。ところが上のサンプルサーブレットはweb.xmlで以下のように登録されています。
<servlet>
<servlet-name>HelloWorldExample</servlet-name>
<servlet-class>HelloWorldExample</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldExample</servlet-name>
<url-pattern>/servlets/servlet/HelloWorldExample</url-pattern>
</servlet-mapping>
このように、
<url-pattern>でPATHを固定に設定していても、上記の方法で任意の文字を挿入することができてしまいます。またweb.xmlに登録されていないJSPファイルについても同様に、
http://localhost:8080/index.jsp;(-_-;)
というリクエストで通常どおり表示されてしまいます。
どういった文字が挿入可能か
Servlet APIのHttpServletrequest#getRequestURI()の説明によればHTTPリクエストをデコードしない、とあるので、リクエストの送り手に依存することになります。
次のようなリクエスト、
http://localhost:8080/test.jsp;!"$&'()*+,-.:<=>@[\]^_`{|}~
を各ブラウザで試した結果は以下のとおりでした。
FireFox(9.0.1) on Windows7
/test.jsp;!%22$&%27()*+,-.:%3C=%3E@%5B%5C%5D%5E_%60%7B|%7D~
IE(8) on Windows7(スラッシュに変換されてしまうので、バックスラッシュは除く)
/test.jsp;!%22$&'()*+,-.:%3C=%3E@[]%5E_%60%7B%7C%7D~
telnet直入力
/test.jsp;!"$&'()*+,-.:<=>@[\]^_`{|}~
インターネットに公開しているサービスなどでは、フロントエンドにApacheを立てて、mod_proxy_ajp等を経由する構成が一般的でしょう。このような構成の場合、デフォルトでは
mod_proxyはURLを正規化します。その結果エンコードされる文字、デコードされる文字については過去に
このブログに書い たので、そちらを参照してください。FireFoxでアクセスした結果は次のようになりました。
mod_proxy
/test.jsp;!%22$&'()*+,-.:%3C=%3E@%5B%5C%5D%5E_%60%7B%7C%7D~
mod_proxy経由で正規化された場合は、ブラウザに依存することなく「
!$&'()*+,-.:;=@_~」がそのまま挿入可能になります。
どの程度危険か
サーブレットコンテナ直接接続の場合、
SQLインジェクションのような直接攻撃(という言い方でいい?)については、telnetでほぼすべての特殊文字が利用可能になります。
XSSのような間接攻撃(?)は利用者(被害者)の使用するブラウザに依存することになります。
mod_proxy 経由の場合、
シングルクォーテーションが入力できることからSQLインジェクションが可能で、XSSについてもタグ内やJavaScript内でシングルクォーテーションで囲まれていればXSS可能になります。いずれの場合も挿入可能な文字種「
!$&'()*+,-.:;=@_~」からすると、かなりのことができそうです。
対策
当たり前のことですが、getRequestURI()からの戻りはクライアントから送られてくるものなので、基本疑ってかかる。これに尽きると思います。適切にエスケープ、エンコード処理を行うべきでしょう。
参考
http://www.springsource.com/security/cve-2010-3700
CVE-2010-3700ではパス中のセミコロン以降に文字列を追加することでSpring Securityの制限がバイパスされてしまう問題を報告しています。
https://superevr.com/blog/2011/three-semicolon-vulnerabilities/
どうしてもこの件書けない事情があり、書けるようになるまで待ってたら、去年の11月に先に書かれたw
getRequestURI()以外の問題についても書かれています。