jetty.project icon indicating copy to clipboard operation
jetty.project copied to clipboard

Error 400 - Ambiguous URI Empty Segment

Open Justvuur opened this issue 1 year ago • 6 comments

Jetty Version 12.0.3

Jetty Environment ee8

Java Version JavaSE-17

Question I just migrated from Jetty 10.0.15 to 12.0.3 and I keep getting the following error: URI: /badURI STATUS: 400 MESSAGE: Ambiguous URI empty segment CAUSED BY: org.eclipse.jetty.http.BadMessageException: 400: Ambiguous URI empty segment

Any ideas what could be causing this?

ambiguous_error

Justvuur avatar Jan 22 '24 10:01 Justvuur

What is your URI?

joakime avatar Jan 22 '24 16:01 joakime

@Justvuur It means your URI has an empty segment (//) which makes it ambiguous. This is because it could be an attempt to bypass some security constraints.

You can set the URI compliance mode in the HttpConfiguration.

See https://eclipse.dev/jetty/javadoc/jetty-12/org/eclipse/jetty/http/UriCompliance.Violation.html#AMBIGUOUS_EMPTY_SEGMENT

When allowing this Violation, the application developer/deployer must ensure that the application behaves as desired when it receives a URI path containing //. Specifically, any URI pattern matching for security concerns needs to be carefully audited.

lachlan-roberts avatar Jan 29 '24 02:01 lachlan-roberts

If any vaiolation of UriCompliance exists at entry point of ServletContextRequest , Jetty12 using Servlet returns 400 Bad Request. So, Whether HttpConfiguration is set or not, any violation is not allowed implicitly with using Servlet. It might be good that UriCompliance check should be taken into account about value of HttpConfiguration#getHttpCompliance

https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java#L195-L208

If you need shorthand workaround, make EmptySegmentHandler and make it handle before ServletContextHandler

public class UriComplianceCheckHandler extends Handler.Wrapper {

    public UriComplianceCheckHandler(Handler handler) {
        super(handler);
    }

    @Override
    public boolean handle(Request request, Response response, Callback callback) throws Exception {
        if (request.getHttpURI().hasViolations()) {
            return super.handle(rewriteRequest(request), response, callback);
        } else {
            return super.handle(request, response, callback);
        }
    }

    Request rewriteRequest(Request request) {
        log.warn("There are HttpURI Violation exists. HttpURI:{}. Violations:{}", request.getHttpURI(),
                request.getHttpURI().getViolations());
        if (request.getHttpURI().hasViolation(UriCompliance.Violation.AMBIGUOUS_EMPTY_SEGMENT)) {
            HttpURI rewrite = rewriteHttpURL(request);
            return Request.serveAs(request, rewrite);
        }
        return request;
    }

    private HttpURI rewriteHttpURL(Request base) {
        String param = base.getHttpURI().getParam();
        String query = base.getHttpURI().getQuery();
        List<String> segments = Arrays
                .stream(base.getHttpURI().getPath().split("\\/"))
                .filter(v -> !v.isEmpty())
                .toList();
        String newPath = "/" + StringUtils.join(segments, "/");

        return HttpURI.build(base.getHttpURI(), newPath, param, query).asImmutable();
    }
}

yokotaso avatar Feb 05 '24 09:02 yokotaso

URI

http://localhost:9998/static/

Justvuur avatar Feb 12 '24 05:02 Justvuur

Ok, I managed to get it working but using http.setUriCompliance(UriCompliance.LEGACY); but I noticed that something somewhere is appending the extra "/". Even though I navigate to "http://localhost:9998/static/", it ends up being "http://localhost:9998/static//".

Any ideas why?

Justvuur avatar Feb 12 '24 06:02 Justvuur

If you see http://localhost:9998/static// with UriCompliance.LEGACY, then that means that is exactly the path as it was sent on the HTTP request from your User-Agent (HTTP Client). That would also explain the "400: Ambiguous URI empty segment" you were seeing. That last segment // is valid per URI rules, and it means a segment with no value (hence ambiguous). It also invalid per Servlet rules, as it cannot match a context-path, url-pattern, or constraint.

To address this problem you have to address what is going on with your HTTP Client. You can confirm this easily with a network traffic capturing tool like wireshark.

Jetty does not "clean up" these kinds of URIs as there are some libraries that rely on this ambiguity (esp security frameworks).

joakime avatar Feb 12 '24 13:02 joakime