【问题标题】:Java embedded jetty is accepting HTTP TRACE methodJava 嵌入式码头正在接受 HTTP TRACE 方法
【发布时间】:2015-03-30 14:06:09
【问题描述】:

我正在尝试禁用嵌入式 Jetty 中的 HTTP TRACE 方法。在 Jetty 文档中,默认情况下 HTTP 跟踪是禁用的,但对于嵌入式,它仍然是启用的。我试图禁用跟踪作为安全约束,就像在 jetty.xml 中所做的那样。

    ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
    servletHandler.setClassLoader(Server.class.getClassLoader());
    servletHandler.setContextPath("/");
    servletHandler.addEventListener(new ContextLoaderListener());
    servletHandler.addServlet(new ServletHolder(new CXFServlet()), "/*");
    servletHandler.setInitParameter("contextClass", AnnotationConfigWebApplicationContext.class.getName());
    servletHandler.setInitParameter("contextConfigLocation", BeansConfig.class.getName());
    servletHandler.setInitParameter("javax.ws.rs.Application", DispatcherConfig.class.getName());

     /*
     * <security-constraint>
     * <web-resource-collection>
     * <web-resource-name>Disable TRACE</web-resource-name>
     * <url-pattern>/</url-pattern>
     * <http-method>TRACE</http-method>
     * </web-resource-collection>
     * <auth-constraint/>
     * </security-constraint>
     */
     Constraint constraint = new Constraint();
     constraint.setName("Disable TRACE");

     ConstraintMapping mapping = new ConstraintMapping();
     mapping.setConstraint(constraint);
     mapping.setMethod("TRACE");
     mapping.setPathSpec("/"); // this did not work same this mapping.setPathSpec("/*");

     ConstraintSecurityHandler securityHandler = (ConstraintSecurityHandler) servletHandler.getSecurityHandler();
     securityHandler.addConstraintMapping(mapping);

soapUI 的示例输出:

HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 143
Server: Jetty(9.0.6.v20130930)

TRACE / HTTP/1.1
Connection: keep-alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Host: 192.168.33.115
Accept-Encoding: gzip,deflate

【问题讨论】:

    标签: spring http jetty trace embedded-jetty


    【解决方案1】:

    扩展 Server 类并覆盖 handle() 方法对我来说效果最好。

    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.server.HttpChannel;
    import org.eclipse.jetty.server.Request;
    import org.eclipse.jetty.server.Response;
    import org.eclipse.jetty.server.Server;
    
    public class MyServer extends Server {
    
        @Override
        public void handle(HttpChannel<?> connection) throws IOException, ServletException {
            Request request=connection.getRequest();
            Response response=connection.getResponse();
    
            if ("TRACE".equals(request.getMethod())){
                request.setHandled(true);
                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            } else {
                super.handle(connection);
            }
        }
    }
    

    【讨论】:

    • HttpConfiguration.Customizer 可能更合适、更容易且更具未来性。 (多年来,Server.handle() API 发生了几次变化)
    【解决方案2】:

    在您的Constraint 对象上,您需要调用setAuthenticate(true),并确保您不调用setRoles(String[])。这使它相当于一个&lt;security-constraint&gt; 和一个空的&lt;auth-constraint&gt;,它禁止访问。

    它适用于DefaultServlet 而不是CXFServlet 的原因是DefaultServlet 明确拒绝对TRACE 方法的访问。

    【讨论】:

      【解决方案3】:

      您可以创建一个过滤器:

      ...
      @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              HttpServletRequest httpRequest = (HttpServletRequest) request;
              HttpServletResponse httpResponse = (HttpServletResponse) response;
              if ("TRACE".equalsIgnoreCase(httpRequest.getMethod())) {
                  httpResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                  return;
              }
              chain.doFilter(request, response);
          }
      ....
      

      【讨论】:

      • 这就是我要做的,因为它不依赖于 Jetty 或其他任何东西。此外,推理也很简单。
      • 如果您决定走这条路……请注意,过滤器不会应用于非上下文请求,并且过滤器行为受 DispatcherType 规则的约束(这经常被忽略)。
      【解决方案4】:

      Jan 的回答有效(当然)。但是,它会导致码头在启动时打印警告。

      WARNING ... SecurityHandler ... has uncovered http methods for path ...
      

      为避免这种情况,首先添加 Jan 的答案中的约束(禁用 TRACE),然后添加另一个约束(允许除 TRACE 之外的所有内容)。我为每个ServletContextHandler 调用以下代码:

      private void disableTraceMethodForHandler(final ServletContextHandler servletContextHandler) {
          SecurityHandler securityHandler = servletContextHandler.getSecurityHandler();
          if (securityHandler == null) {
              securityHandler = new ConstraintSecurityHandler();
              servletContextHandler.setSecurityHandler(securityHandler);
          }
          if (securityHandler instanceof ConstraintSecurityHandler) {
              ConstraintSecurityHandler constraintSecurityHandler = (ConstraintSecurityHandler) securityHandler;
      
              ConstraintMapping disableTraceMapping = new ConstraintMapping();
              Constraint disableTraceConstraint = new Constraint();
              disableTraceConstraint.setName("Disable TRACE");
              disableTraceConstraint.setAuthenticate(true);
              disableTraceMapping.setConstraint(disableTraceConstraint);
              disableTraceMapping.setPathSpec("/");
              disableTraceMapping.setMethod("TRACE");
              constraintSecurityHandler.addConstraintMapping(disableTraceMapping);
      
              ConstraintMapping enableEverythingButTraceMapping = new ConstraintMapping();
              Constraint enableEverythingButTraceConstraint = new Constraint();
              enableEverythingButTraceConstraint.setName("Enable everything but TRACE");
              enableEverythingButTraceMapping.setConstraint(enableEverythingButTraceConstraint);
              enableEverythingButTraceMapping.setMethodOmissions(new String[] {"TRACE"});
              enableEverythingButTraceMapping.setPathSpec("/");
              constraintSecurityHandler.addConstraintMapping(enableEverythingButTraceMapping);
          }
      }
      

      在 Google 的 appengine-java-vm-runtime 和 the fix 中找到 this issue 后,我找到了这个解决方案,两者都是 1 月之前的。上面的代码应该与那里的 XML 配置相同。

      【讨论】:

      • 这确实有效,但是它会抛出 403(禁止)而不是更合适的 405(不允许的方法)
      【解决方案5】:

      您可以使用HttpConfiguration.Customizer,而不是使用约束。

      好处是这将适用于所有请求,即使它们不属于某个上下文,并且也适用于所有上下文特定的 Web 应用程序。

      package jetty;
      
      import java.io.IOException;
      import java.net.URI;
      import java.net.http.HttpClient;
      import java.net.http.HttpRequest;
      import java.net.http.HttpResponse;
      import java.util.EnumSet;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import org.eclipse.jetty.http.HttpMethod;
      import org.eclipse.jetty.http.HttpStatus;
      import org.eclipse.jetty.server.Connector;
      import org.eclipse.jetty.server.HttpConfiguration;
      import org.eclipse.jetty.server.HttpConnectionFactory;
      import org.eclipse.jetty.server.Request;
      import org.eclipse.jetty.server.Server;
      import org.eclipse.jetty.server.ServerConnector;
      import org.eclipse.jetty.server.handler.AbstractHandler;
      import org.eclipse.jetty.server.handler.DefaultHandler;
      import org.eclipse.jetty.server.handler.HandlerList;
      
      public class RejectHttpMethodsDemo
      {
          public static class BanHttpMethods implements HttpConfiguration.Customizer
          {
              private final EnumSet<HttpMethod> bannedMethods;
      
              public BanHttpMethods(EnumSet<HttpMethod> bannedMethods)
              {
                  this.bannedMethods = bannedMethods;
              }
      
              @Override
              public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
              {
                  HttpMethod httpMethod = HttpMethod.fromString(request.getMethod());
                  if (bannedMethods.contains(httpMethod))
                  {
                      request.setHandled(true);
                      request.getResponse().setStatus(HttpStatus.METHOD_NOT_ALLOWED_405);
                  }
              }
          }
      
          public static void main(String[] args) throws Exception
          {
              Server server = new Server();
      
              HttpConfiguration httpConfig = new HttpConfiguration();
              httpConfig.addCustomizer(new BanHttpMethods(EnumSet.of(HttpMethod.TRACE, HttpMethod.MOVE)));
              ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
              connector.setPort(9090);
              server.addConnector(connector);
      
              HandlerList handlers = new HandlerList();
              handlers.addHandler(new AbstractHandler()
              {
                  @Override
                  public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
                  {
                      baseRequest.setHandled(true);
                      response.setCharacterEncoding("utf-8");
                      response.setContentType("text/plain");
                      response.getWriter().printf("Hello, You asked to %s %s, that is all%n", baseRequest.getMethod(), baseRequest.getRequestURI());
                  }
              });
              handlers.addHandler(new DefaultHandler());
      
              server.setHandler(handlers);
              server.start();
      
              try
              {
                  HttpClient httpClient = HttpClient.newHttpClient();
      
                  demoRequest(httpClient, server.getURI().resolve("/apple"), "GET");
                  demoRequest(httpClient, server.getURI().resolve("/banana"), "TRACE");
                  demoRequest(httpClient, server.getURI().resolve("/cherry"), "MOVE");
              }
              catch (Throwable t)
              {
                  t.printStackTrace();
              }
              finally
              {
                  server.stop();
              }
          }
      
          private static void demoRequest(HttpClient httpClient, URI path, String method)
          {
              try
              {
                  HttpRequest httpRequest = HttpRequest.newBuilder(path)
                      .method(method, HttpRequest.BodyPublishers.noBody())
                      .build();
                  HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
                  System.out.printf("HTTP %s %s Response Status: %d%n", httpRequest.method(), httpRequest.uri(), httpResponse.statusCode());
                  System.out.println(httpResponse.body());
              }
              catch (IOException | InterruptedException e)
              {
                  e.printStackTrace();
              }
          }
      }
      

      有输出...

      2022-01-21 11:23:13.718:INFO::main: Logging initialized @210ms to org.eclipse.jetty.util.log.StdErrLog
      2022-01-21 11:23:13.775:INFO:oejs.Server:main: jetty-9.4.43.v20210629; built: 2021-06-30T11:07:22.254Z; git: 526006ecfa3af7f1a27ef3a288e2bef7ea9dd7e8; jvm 11.0.12+7
      2022-01-21 11:23:13.809:INFO:oejs.AbstractConnector:main: Started ServerConnector@4fcd19b3{HTTP/1.1, (http/1.1)}{0.0.0.0:9090}
      2022-01-21 11:23:13.812:INFO:oejs.Server:main: Started @308ms
      HTTP GET http://127.0.1.1:9090/apple Response Status: 200
      Hello, You asked to GET /apple, that is all
      
      HTTP TRACE http://127.0.1.1:9090/banana Response Status: 405
      
      HTTP MOVE http://127.0.1.1:9090/cherry Response Status: 405
      
      2022-01-21 11:23:14.186:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@4fcd19b3{HTTP/1.1, (http/1.1)}{0.0.0.0:9090}
      

      【讨论】:

        猜你喜欢
        • 2018-07-02
        • 1970-01-01
        • 1970-01-01
        • 2014-02-25
        • 2019-09-25
        • 1970-01-01
        • 2021-11-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多