-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"Ambiguous URI path encoding" when upgrading to Jetty 12 EE8 #12162
Comments
The reason Servlet 6 rejects these sequences is that is causes bugs in Servlet land that cannot be resolved or fixed without big changes to the Servlet API. When things are ambiguous in the path section, then things like security constraints, url-patterns, redirect location logic, and various values on HttpServletRequest are bad and you cannot tell the difference between someone using a raw These bugs also exist in all prior versions of Servlet too, and it is not possible to resolve them. Regarding your specific case, that seems to be triggering the Using Confirmed on fresh jetty-base running Jetty 12.0.12 with Default behavior
Using configuration to allow it.
|
The behavior you are experiencing is coming from package uri;
import java.net.URI;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.UriCompliance;
public class JettyUriTest
{
public static void main(String[] args)
{
String name = "5boards.txt";
dump("http://localhost/foo/%" + name);
dump("http://localhost/foo/%25" + name);
dump("http://localhost/foo/%2525" + name);
}
public static void dump(String rawinput)
{
System.out.println("\n--- raw input \"" + rawinput + "\" ---");
dump(HttpURI.build(rawinput));
dump(URI.create(rawinput));
}
private static void dump(HttpURI uri)
{
System.out.println("Jetty HttpURI = " + uri);
System.out.println(" .toString = " + uri.toString());
// System.out.println(" .isAbsolute = " + uri.isAbsolute());
// System.out.println(" .scheme = " + uri.getScheme());
// System.out.println(" .authority = " + uri.getAuthority());
// System.out.println(" .user = " + uri.getUser());
// System.out.println(" .host = " + uri.getHost());
// System.out.println(" .port = " + uri.getPort());
System.out.println(" .path: " + uri.getPath());
System.out.println(" .decodedPath: " + uri.getDecodedPath());
if (uri.hasViolations())
{
System.out.println(" -VIOLATIONS-");
for (UriCompliance.Violation violation : uri.getViolations())
{
System.out.println(" * violation = " + violation);
}
}
}
private static void dump(URI uri)
{
System.out.println("java.net.URI = " + uri);
System.out.println(" .toASCIIString = " + uri.toASCIIString());
// System.out.println(" .isAbsolute = " + uri.isAbsolute());
// System.out.println(" .scheme = " + uri.getScheme());
// System.out.println(" .authority = " + uri.getAuthority());
// System.out.println(" .userInfo = " + uri.getUserInfo());
// System.out.println(" .host = " + uri.getHost());
// System.out.println(" .port = " + uri.getPort());
System.out.println(" .rawPath: " + uri.getRawPath());
System.out.println(" .path = " + uri.getPath());
System.out.println(" .schemeSpecificPart = " + uri.getSchemeSpecificPart());
System.out.println(" .normalize = " + uri.normalize());
}
} With the output ...
|
Demo of this setup in Jetty 12 embedded. package uri;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
public class JettyEncodedPercentDemo
{
public static void main(String[] args) throws Exception
{
Server server = new Server();
HttpConfiguration httpConfiguration = new HttpConfiguration();
UriCompliance specViolatingCompliance = UriCompliance.from("RFC3986,AMBIGUOUS_PATH_ENCODING");
httpConfiguration.setUriCompliance(specViolatingCompliance);
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
connector.setPort(8080);
server.addConnector(connector);
Handler.Abstract handler = new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
response.setStatus(200);
response.getHeaders().add(HttpHeader.CONTENT_TYPE, "text/plain;charset=utf-8");
StringBuilder str = new StringBuilder();
str.append("Got Raw URI: ").append(request.getHttpURI().toString());
str.append("\nDecoded Path: ").append(request.getHttpURI().getDecodedPath());
Content.Sink.write(response, true, str.toString(), callback);
return true;
}
};
server.setHandler(handler);
try
{
server.start();
URI serverURI = server.getURI();
String name = "5boards.txt";
sendRequest(serverURI.getHost(), serverURI.getPort(), "/foo/%" + name);
sendRequest(serverURI.getHost(), serverURI.getPort(), "/foo/%25" + name);
sendRequest(serverURI.getHost(), serverURI.getPort(), "/foo/%2525" + name);
}
finally
{
LifeCycle.stop(server);
}
}
private static void sendRequest(String host, int port, String path) throws IOException
{
try (Socket client = new Socket(host, port);
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream())
{
String rawRequest = """
GET %s HTTP/1.1
Host: %s:%d
Connection: close
""".formatted(path, host, port);
System.out.println("\n----- REQUEST -----\n" + rawRequest);
out.write(rawRequest.getBytes(StandardCharsets.UTF_8));
out.flush();
String rawResponse = IO.toString(in, StandardCharsets.UTF_8);
System.out.println("----- RESPONSE -----\n" + rawResponse);
}
}
} With output ...
|
Thanks a lot @joakime for your answer (and a fast one), much appreciated :)
Ok, but here since I'm using EE8, I'm on Servlet 4 so the rules for Servlet 6 shouldn't apply, right? Aren't they the same rules that were used in Jetty 10?
Unfortunately it didn't help. I had tried a lot of values, including using I'm still getting:
Any idea of what I could try? |
twas an easy one to answer too, and I happened to have these demo projects already written. :)
The bugs exists in the Servlet API (no specific version). The "any ambiguous encoding in the path portion" is any encoded character that impacts URI reserved characters or the The bug is not unique to Servlet 6, it exists all the way back to Servlet 1.0 too.
The "Ambiguous URI path encoding" message applies to all ambiguous path issues.
The fact that you see I suspect you are not setting that configuration either in the right place (so it isn't applied to the same connector you are testing), or the right time (so it isn't applied), or something else. I was going to have you look at the HttpConfiguration at runtime, but discovered those configurations are not present in either the dump or JMX (grumble grumble), so I opened #12163 |
Works if I modify |
How can I know if the |
I guess it's used since otherwise the So maybe the issue is just the syntax for defining a property in |
Knowing that this used to work in Jetty 10, maybe there's been a change in Jetty 12? |
If I remove our
We have:
|
The You can verify the configuration with
All three of those must be absolute paths, not relative. |
Also, you should never have the jetty-home or the jetty-base nested inside each other in either direction. This is bad.
This is also bad.
It should be ...
|
Thanks @joakime for the recommendations. However, this shouldn't affect why the This is what I got with
Any idea what could be wrong? Thx |
Probably because
|
As you can see in your output ...
That property is being placed into the Another way to see this ...
|
Thanks @joakime I've now removed the following from
All working! |
Only remaining question I guess is whether there's a syntax to force a property to be defined as core? |
That would mean 2 separate modules (one for server, one for ee8)
|
Thanks @joakime |
re-opening to see if we can improve documentation |
@vmassol the problem, as @joakime pointed out, is that you have defined a a) if specifying things on the command line, the property must be defined before the module or xml file that uses it I think we should update our doco to be clearer about this change. |
@janbartel Thanks a lot for the additional explanations! So far I've removed the TBH I'm not sure what adding the Thanks again |
Jetty Version
Jetty 12.0.12
Jetty Environment
EE8
Java Version
17
Question
On XWiki, I'm trying to upgrade from Jetty 10 to Jetty 12.0.12 and one of our tests is failing when
http://localhost:8080/xwiki/rest/wikis/xwiki/spaces/AttachmentsResourceIT/pages/testPUTGETAttachments/attachments/%25percent.txt
is called. It leads to a 400 error with the message "Ambiguous URI path encoding".The problem is that we've set
jetty.httpConfig.uriCompliance=RFC3986
(we had that in Jetty 10 already) but it's not helping.I've read:
I'm still puzzled and don't understand what we can do to allow
%
to be passed in URLs in Jetty 12. In the threads above it's mentioned that the issue is that Servlet 6 spec doesn't allow it. That's fine but I don't fully understand why theRFC3986
compliance doesn't allow it (I've also tried other values likejetty.httpConfig.uriCompliance=RFC3986,AMBIGUOUS_PATH_ENCODING,AMBIGUOUS_PATH_SEPARATOR
), but more importantly since we've configured Jetty to use EE8 (ie Servlet 4), I don't understand why we would be subject to Servlet 6 rules :)Any idea?
Thanks a lot
The text was updated successfully, but these errors were encountered: