// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletTester; import org.hamcrest.Matchers; import org.junit.Test; import com.acme.DispatchServlet; /** * Simple tests against DispatchServlet. */ public class DispatchServletTest { /** * As filed in JETTY-978. * * Security problems in demo dispatch servlet. * * <blockquote> * <p> * The dispatcher servlet (com.acme.DispatchServlet) is prone to a Denial of * Service vulnerability. * </p> * <p> * This example servlet is meant to be used as a resources dispatcher, * however a malicious aggressor may abuse this functionality in order to * cause a recursive inclusion. In details, it is possible to abuse the * method com.acme.DispatchServlet.doGet(DispatchServlet.java:203) forcing * the application to recursively include the "Dispatch" servlet. * </p> * <p> * Dispatch com.acme.DispatchServlet 1 Dispatch /dispatch/* As a result, it * is possible to trigger a "java.lang.StackOverflowError" and consequently * an internal server error (500). * </p> * <p> * Multiple requests may easily affect the availability of the servlet * container. Since this attack can cause the server to consume resources in * a non-linear relationship to the size of inputs, it should be considered * as a server flaw. * </p> * <p> * The vulnerability seems confined to the example servlet and it does not * afflict the Jetty's core." * </p> * </blockquote> * * @throws Exception */ @Test public void testSelfRefForwardDenialOfService() throws Exception { ServletTester tester = new ServletTester(); tester.setContextPath("/tests"); ServletHolder dispatch = tester.addServlet(DispatchServlet.class,"/dispatch/*"); tester.addServlet(DefaultServlet.class,"/"); tester.start(); StringBuilder req1 = new StringBuilder(); req1.append("GET /tests/dispatch/includeN/").append(dispatch.getName()).append(" HTTP/1.1\n"); req1.append("Host: tester\n"); req1.append("Connection: close\n"); req1.append("\n"); String response = tester.getResponses(req1.toString()); String msg = "Response code on SelfRefDoS"; assertFalse(msg + " should not be code 500.",response.startsWith("HTTP/1.1 500 ")); assertTrue(msg + " should return error code 403 (Forbidden)", response.startsWith("HTTP/1.1 403 ")); } @Test public void testSelfRefDeep() throws Exception { ServletTester tester = new ServletTester(); tester.setContextPath("/tests"); tester.addServlet(DispatchServlet.class,"/dispatch/*"); tester.addServlet(DefaultServlet.class,"/"); tester.start(); String selfRefs[] = { "/dispatch/forward", "/dispatch/includeS", "/dispatch/includeW", "/dispatch/includeN", }; /* * Number of nested dispatch requests. 220 is a good value, as it won't * trigger an Error 413 response (Entity too large). Anything larger * than 220 will trigger a 413 response. */ int nestedDepth = 220; for (String selfRef : selfRefs) { StringBuilder req1 = new StringBuilder(); req1.append("GET /tests"); for (int i = 0; i < nestedDepth; i++) { req1.append(selfRef); } req1.append("/ HTTP/1.1\n"); req1.append("Host: tester\n"); req1.append("Connection: close\n"); req1.append("\n"); String response = tester.getResponses(req1.toString()); StringBuilder msg = new StringBuilder(); msg.append("Response code on nested \"").append(selfRef).append("\""); msg.append(" (depth:").append(nestedDepth).append(")"); assertFalse(msg + " should not be code 413 (Request Entity Too Large)," + "the nestedDepth in the TestCase is too large (reduce it)", response.startsWith("HTTP/1.1 413 ")); assertFalse(msg + " should not be code 500.", response.startsWith("HTTP/1.1 500 ")); assertThat(response,Matchers.startsWith("HTTP/1.1 403 ")); } } }