Magnolia Tech

いつもコードのことばかり考えている人のために。

Percent-Encodingされた'%2F'は'/'と等価なのか

Scalatraで使っているJettyのバージョンを9.4.37.v20210219に上げたところ、テストが通らなくなってしまった。直後にリリースされた9.4.38.v20210224ではまた通るようになった。何が起きたのか?

ポイントは、このPRだ。

Fix #6001 separate compliance modes for ambiguous URI segments, params and separators by gregw · Pull Request #6005 · eclipse/jetty.project · GitHub

このPRは以下のPRの内容を一部元に戻す変更を取り入れている。

Fix #4275 #6001 separate compliance modes for ambiguous URI segments and se… by gregw · Pull Request #6003 · eclipse/jetty.project · GitHub


元ととなった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環境でサーブレットコンテナが違っている、という場合は要注意です(あんまりない気がするけど)。