diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml index f5a59870251b..aea0e497a143 100644 --- a/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-server/src/main/config/etc/jetty.xml @@ -74,6 +74,7 @@ + diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod index ca5a2258120d..ee47c0f883ce 100644 --- a/jetty-server/src/main/config/modules/server.mod +++ b/jetty-server/src/main/config/modules/server.mod @@ -72,6 +72,9 @@ patch-module: servlet.api=lib/jetty-schemas-3.1.jar ## multipart/form-data compliance mode of: LEGACY(slow), RFC7578(fast) # jetty.httpConfig.multiPartFormDataCompliance=LEGACY +## Relative Redirection Locations allowed +# jetty.httpConfig.relativeRedirectionAllowed=false + ### Server configuration ## Whether ctrl+c on the console gracefully stops the Jetty server # jetty.server.stopAtShutdown=true diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index fddf9c4a89c0..a4508c6a96fd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -76,6 +76,7 @@ public class HttpConfiguration implements Dumpable private CookieCompliance _responseCookieCompliance = CookieCompliance.RFC6265; private MultiPartFormDataCompliance _multiPartCompliance = MultiPartFormDataCompliance.LEGACY; // TODO change default in jetty-10 private boolean _notifyRemoteAsyncErrors = true; + private boolean _relativeRedirectionAllowed; /** *

An interface that allows a request object to be customized @@ -142,6 +143,7 @@ public HttpConfiguration(HttpConfiguration config) _responseCookieCompliance = config._responseCookieCompliance; _multiPartCompliance = config._multiPartCompliance; _notifyRemoteAsyncErrors = config._notifyRemoteAsyncErrors; + _relativeRedirectionAllowed = config._relativeRedirectionAllowed; } /** @@ -642,6 +644,23 @@ public boolean isNotifyRemoteAsyncErrors() return _notifyRemoteAsyncErrors; } + /** + * @param allowed True if relative redirection locations are allowed + */ + public void setRelativeRedirectionAllowed(boolean allowed) + { + _relativeRedirectionAllowed = allowed; + } + + /** + * @return True if relative redirection locations are allowed + */ + @ManagedAttribute("Whether relative redirection locations are allowed") + public boolean isRelativeRedirectionAllowed() + { + return _relativeRedirectionAllowed; + } + @Override public String dump() { @@ -673,7 +692,8 @@ public void dump(Appendable out, String indent) throws IOException "minResponseDataRate=" + _minResponseDataRate, "cookieCompliance=" + _requestCookieCompliance, "setRequestCookieCompliance=" + _responseCookieCompliance, - "notifyRemoteAsyncErrors=" + _notifyRemoteAsyncErrors + "notifyRemoteAsyncErrors=" + _notifyRemoteAsyncErrors, + "relativeRedirectionAllowed=" + _relativeRedirectionAllowed ); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 94148f3b5b56..ccb0f8962abc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -506,7 +506,9 @@ public void sendRedirect(int code, String location) throws IOException if (!URIUtil.hasScheme(location)) { - StringBuilder buf = _channel.getRequest().getRootURL(); + StringBuilder buf = _channel.getHttpConfiguration().isRelativeRedirectionAllowed() + ? new StringBuilder() + : _channel.getRequest().getRootURL(); if (location.startsWith("/")) { // absolute in context diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index 5a8ff294ace5..2571625ed71b 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -858,6 +858,80 @@ public void testSendRedirect() } } + @Test + public void testSendRedirectRelative() + throws Exception + { + String[][] tests = { + // No cookie + { + "http://myhost:8888/other/location;jsessionid=12345?name=value", + "http://myhost:8888/other/location;jsessionid=12345?name=value" + }, + {"/other/location;jsessionid=12345?name=value", "/other/location;jsessionid=12345?name=value"}, + {"./location;jsessionid=12345?name=value", "/path/location;jsessionid=12345?name=value"}, + + // From cookie + {"/other/location", "/other/location"}, + {"/other/l%20cation", "/other/l%20cation"}, + {"location", "/path/location"}, + {"./location", "/path/location"}, + {"../location", "/location"}, + {"/other/l%20cation", "/other/l%20cation"}, + {"l%20cation", "/path/l%20cation"}, + {"./l%20cation", "/path/l%20cation"}, + {"../l%20cation", "/l%20cation"}, + {"../locati%C3%abn", "/locati%C3%abn"}, + {"../other%2fplace", "/other%2fplace"}, + {"http://somehost.com/other/location", "http://somehost.com/other/location"}, + }; + + int[] ports = new int[]{8080, 80}; + String[] hosts = new String[]{null, "myhost", "192.168.0.1", "0::1"}; + for (int port : ports) + { + for (String host : hosts) + { + for (int i = 0; i < tests.length; i++) + { + // System.err.printf("%s %d %s%n",host,port,tests[i][0]); + + Response response = getResponse(); + Request request = response.getHttpChannel().getRequest(); + request.getHttpChannel().getHttpConfiguration().setRelativeRedirectionAllowed(true); + + request.setScheme("http"); + if (host != null) + request.setAuthority(host, port); + request.setURIPathQuery("/path/info;param;jsessionid=12345?query=0&more=1#target"); + request.setContextPath("/path"); + request.setRequestedSessionId("12345"); + request.setRequestedSessionIdFromCookie(i > 2); + SessionHandler handler = new SessionHandler(); + + NullSessionDataStore ds = new NullSessionDataStore(); + DefaultSessionCache ss = new DefaultSessionCache(handler); + handler.setSessionCache(ss); + ss.setSessionDataStore(ds); + DefaultSessionIdManager idMgr = new DefaultSessionIdManager(_server); + idMgr.setWorkerName(null); + handler.setSessionIdManager(idMgr); + request.setSessionHandler(handler); + request.setSession(new TestSession(handler, "12345")); + handler.setCheckingRemoteSessionIdEncoding(false); + + response.sendRedirect(tests[i][0]); + + String location = response.getHeader("Location"); + + String expected = tests[i][1] + .replace("@HOST@", host == null ? request.getLocalAddr() : (host.contains(":") ? ("[" + host + "]") : host)) + .replace("@PORT@", host == null ? ":8888" : (port == 80 ? "" : (":" + port))); + assertEquals(expected, location, "test-" + i + " " + host + ":" + port); + } + } + } + } @Test public void testInvalidSendRedirect() throws Exception {