Scalatraで使っているJettyのバージョンを9.4.37.v20210219に上げたところ、テストが通らなくなってしまった。直後にリリースされた9.4.38.v20210224ではまた通るようになった。何が起きたのか?
ポイントは、このPRだ。
このPRは以下のPRの内容を一部元に戻す変更を取り入れている。
元ととなったPRでは、URLにおける..;
と..
を等価に扱うべきではないという主張に基づき、複数の解釈の余地が有る紛らわしい表記である..;
を無効なURLとしよう、という内容だった(RFC3986ではあくまで..
はディレクトリを一つ上に戻すと書いているが、..;
で戻しても良いとは書かれていない)。
あまり見かけないけど、URLではpathのsegmentにも;
を使ってパラメータを付加する、という考え方が有る(例えば、name;v=1.1
のように)。RFC3986の中でも、しばしば使われる、程度の表現しかしていない。
そして合わせて”同様に複数の解釈が可能な表現を制限すべきでは?"という考え方から、Percent-Encodingされた.
と..
と/
が無効なURLとなった。つまり、%2E%2E
と%2F
だ。
Scalatraでは%2F
がURLに含まれるパターンのテストを用意しているので、これを組み込みのJettyでテストしようとしたところ、一律無効なURLとして判定されエラーになっていた。
直後でさすがに既存のコードへの影響が大きいということで、Percent-Encodingされた.
と..
と/
を無効にする件は元に戻された。
しかし、一連のPRについているコメントを見ると、Percent-Encodingされた.
と..
と/
を通すか否かは、サーバによってだいぶ違っていて、必ず通るわけではないので必ず仕様を調べましょう。ただし、RFC3986ではエンコード前後で等価で扱うべきだし、そもそもこれらの文字はPercent-Encodingすべきではないと書いている。また、そのパースのされた方はサーバの実装によると注意喚起を行っている。
なお、Scalatraでは謎の2段階デコードが行われていたので、それを止めるPRを書いた。
Changed path encoding to be selectable · magnolia-k/scalatra@2d57577 · GitHub
本来ならエンコーディング前後で意味論は変わらないので、デコードしてからアプリケーションに渡しても良いが、それでは互換性が崩れてしまうので、今はpercent-encodingされた状態でアプリに渡るようにしている。Servletの仕様では単純にrequestPathを取ると全てデコード状態で渡されるのだけど、わざわざ自前で作り直している。
というわけで、URLに%2F
と%2E
を含める場合はサーバが通すか否かをきちんと確認しましょう。特にローカルのdev環境とproduction環境でサーブレットコンテナが違っている、という場合は要注意です(あんまりない気がするけど)。