jetty / jetty.project

Eclipse Jetty® - Web Container & Clients - supports HTTP/2, HTTP/1.1, HTTP/1.0, websocket, servlets, and more
https://eclipse.dev/jetty
Other
3.85k stars 1.91k forks source link

"Ambiguous URI encoding" error when calling HttpServletRequest#getServletPath() on jetty-ee10-servlet despite a custom `uriCompliance` #12346

Open tmortagne opened 6 days ago

tmortagne commented 6 days ago

Jetty version(s)

Jetty 12.0.12

Jetty Environment

ee10

Java version/vendor (use: java -version)

openjdk version "21.0.3" 2024-04-16
OpenJDK Runtime Environment (build 21.0.3+9-Ubuntu-1ubuntu1)
OpenJDK 64-Bit Server VM (build 21.0.3+9-Ubuntu-1ubuntu1, mixed mode, sharing)

OS type/version

Ubuntu 24.04.

Description

I modified jetty.xml to set UNSAFE as uriCompliance (not really my target compliance, but wanted to be extra sure) and I can definitely access resources with %2F in the URL for example.

But when I try to use HttpServletRequest#getServletPatht() I end up with the following stack trace:

org.eclipse.jetty.http.HttpException$IllegalArgumentException: 400: Ambiguous URI encoding: AMBIGUOUS_PATH_SEPARATOR
    at org.eclipse.jetty.ee10.servlet.ServletApiRequest$AmbiguousURI.getServletPath(ServletApiRequest.java:1461)
    at jakarta.servlet.http.HttpServletRequestWrapper.getServletPath(HttpServletRequestWrapper.java:213)
    at jakarta.servlet.http.HttpServletRequestWrapper.getServletPath(HttpServletRequestWrapper.java:213)
    at jakarta.servlet.http.HttpServletRequestWrapper.getServletPath(HttpServletRequestWrapper.java:213)
    ...

After some debugging, I tracked the root cause to https://github.com/jetty/jetty.project/blob/jetty-12.0.12/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java#L201:

// TODO we should check if current compliance mode allows all the violations?

Any idea if there is something I can do to workaround this, or the only way is for this TODO to be resolved ?

joakime commented 6 days ago

In ee10, it's not possible to support some Servlet 6 APIs when those ambiguous path violations exist.

This is a bug in the Servlet spec that has existed for years, and has been a source of problems (and security issues) from the day the Servlet spec was created. Over the years there has been efforts on the Servlet spec to correct and address these issues, but it is really not possible to fix, that's why Servlet 6 has effectively banned these kinds of URI.path violations.

If you have a section in your URI.path that has a %2F then move that to the query section, or don't encode it, and use a path segment (like -) that indicates the separation between paths that go to controllers / servlets and paths that belong to the parameters for that endpoint.

Examples.

Original URIs
http://example.org/process/git/branch/fix%2Frefactor

Replacement URIs
http://example.org/process/git/branch?ref=fix/refactor
http://example.org/process/git/branch/-/fix/refactor
joakime commented 6 days ago

Also of note, please check out the examples found at https://github.com/jetty/jetty-examples/

You can find an example of the ServletHandler.setDecodeAmbiguousURIs(true); setting that will decode ambiguous uris before dispatching them to the ServletContext.

See:

This might help in your situation, but it's just a band aid, and ultimately the use of %2F in the path portion of your URI will have to change if you want to continue using the Servlet spec.

gregw commented 6 days ago

Just to be clear (and we should probably document this better, as this is the second such issue), there are two steps involved in accepting ambiguous URIs.

The first is the URI Compliance mode, which controls if requests with ambiguous URIs will be allowed into the server, or if they will be rejected before being handled. Allowing such URIs into the server is not always ambiguous, as applications can be written to work using the encoded (non-ambiguous) APIs.

The second step, is the ServletHandler.setDecodeAmbiguousURIs(true), which controls if we will return such ambiguous URIs via the string based servlet APIs that collapse the "quantum super-position-of-different-URI-interpretation" wave function into a single interpretation.

So the LEGACY mode is only sufficient if you don't use getServletPath and getPathInfo. If you want to use those APIs, you have to turn on the decodeAmbiguousURIs mode and live with the security ramifications that might have.

tmortagne commented 6 days ago

Thanks for the reference to ServletHandler.setDecodeAmbiguousURIs(true); and the example. Is there any way to set this through configuration of a more standard jetty setup, like in the case of uriCompliance which can be set in jetty.xml ? I would prefer to avoid putting jetty specific code in my WAR (provided setting this in my ServletContextListener implementation would not be too late).

the use of %2F in the path portion of your URI will have to change if you want to continue using the Servlet spec.

I understood, and we are working on it, but those URLs are API right now, so we cannot just get rid of them instantly, and we need to make sure to keep them working at least for a little while.