如何在嵌入式Jetty服务器中禁用TRACE?

3
我正在处理一个项目,希望禁用嵌入式Jetty服务器的TRACE方法。结果发现,对于嵌入式服务器,默认情况下并未禁用TRACE方法。
我尝试使用以下源代码创建约束和过滤器: Java embedded jetty is accepting HTTP TRACE method 它可以工作,但当我尝试OPTIONS方法时,它仍然显示TRACE为允许。
我该如何确保OPTIONS方法显示不允许使用TRACE?
注意: OPTIONS返回以下标头; Allow: GET, OPTIONS, TRACE 我想要的是: Allow: GET, OPTIONS
编辑:
ServletContextHandler myContext = new ServletContextHandler();
            
ServletHolder servletHolder = new ServletHolder(new MyServlet());
myContext.addServlet(servletHolder, "/");
myContext.addFilter(MyFilter.class,"/",EnumSet.of(DispatcherType.REQUEST));
contextHandlers.add(myContext);

你是如何使用嵌入式Jetty的(这很重要)?请编辑你的问题并包含你的代码示例,其中包括你的WebAppContext、ServletContextHandler、ServletHandler或者普通的Handler设置。 - Joakim Erdfelt
@JoakimErdfelt 添加了我的ServletContextHandler。 - jhthewow
1个回答

0

这里有一些需要注意的地方...禁用TRACE请求,并从OPTIONS响应头中删除TRACE。

首先,让我们解决简单的问题,禁用TRACE请求。
这可以通过在ServletContext级别上进行约束映射来完成。
这将影响所有Servlet端点上TRACE请求的使用。
请参见下面的示例。

接下来,从OPTIONS Allow头字段中删除TRACE。
这不像禁用TRACE那样容易以全局方式完成。
这是因为每个HttpServlet都有一个默认实现的doOptions(HttpServletRequest, HttpServletResponse),它将始终添加TRACE和OPTIONS到Allow头中,以及您已经为该特定HttpServlet端点启用的任何其他方法。

您可以在此处查看HttpServlet.doOption的实际默认实现...

https://github.com/eclipse-ee4j/servlet-api/blob/4.0.4-RELEASE/api/src/main/java/javax/servlet/http/HttpServlet.java#L361-L432

为什么OPTIONS方法请求总是返回TRACE和OPTIONS? 这是因为在HttpServlet中这些方法的默认实现总是返回响应,所以它们总是被添加到Allow头中。
这意味着OPTIONS是在每个servlet范围内受控制的。 您可以在特定的servlet中覆盖doOptions方法并使其返回您想要的内容,但您不能有一个顶级的约束或过滤器来控制它,因为约束或过滤器都不知道Servlet端点的详细信息,无法正确确定OPTIONS Allow头的其他部分(如GET、HEAD、POST、PUT等)。
以下是一些示例代码,展示了正确的约束映射和可选的doOption覆盖。
package jetty;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.security.Constraint;

import static java.nio.charset.StandardCharsets.UTF_8;

public class NoTraceDemo
{
    public static void main(String[] args)
    {
        Server server = new Server(8888);

        ServletContextHandler servletContextHandler = new ServletContextHandler();
        servletContextHandler.addServlet(HelloServlet.class, "/");
        ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler();
        servletContextHandler.setSecurityHandler(constraintSecurityHandler);

        Constraint constraintDisableTrace = new Constraint();
        constraintDisableTrace.setAuthenticate(true);
        ConstraintMapping mappingDisableTrace = new ConstraintMapping();
        mappingDisableTrace.setPathSpec("/");
        mappingDisableTrace.setMethod("TRACE");
        mappingDisableTrace.setConstraint(constraintDisableTrace);
        constraintSecurityHandler.addConstraintMapping(mappingDisableTrace);

        Constraint constraintEnabledEverythingButTrace = new Constraint();
        ConstraintMapping mappingEnableEverythingButTrace = new ConstraintMapping();
        mappingEnableEverythingButTrace.setPathSpec("/");
        mappingEnableEverythingButTrace.setMethodOmissions(new String[]{"TRACE"});
        mappingEnableEverythingButTrace.setConstraint(constraintEnabledEverythingButTrace);
        constraintSecurityHandler.addConstraintMapping(mappingEnableEverythingButTrace);

        server.setHandler(servletContextHandler);
        try
        {
            server.start();

            URL url = server.getURI().toURL();
            request("TRACE", url);
            request("OPTIONS", url);
            request("GET", url);
        }
        catch (Throwable t)
        {
            t.printStackTrace();
        }
        finally
        {
            LifeCycle.stop(server);
        }
    }

    private static void request(String method, URL url)
    {
        try
        {
            HttpURLConnection http = (HttpURLConnection)url.openConnection();
            http.setDoOutput(true);
            http.setRequestMethod(method);

            System.out.println("---------");
            System.out.printf("%s %s HTTP/1.1%n", http.getRequestMethod(), http.getURL());
            System.out.println("----");
            System.out.printf("%s%n", http.getHeaderField(null));
            http.getHeaderFields().entrySet().stream()
                .filter(entry -> entry.getKey() != null)
                .forEach((entry) -> System.out.printf("%s: %s%n", entry.getKey(), http.getHeaderField(entry.getKey())));
            InputStream bodyStream = (http.getResponseCode() == 200) ? http.getInputStream() : http.getErrorStream();
            if (bodyStream != null)
            {
                String body = IO.toString(bodyStream, UTF_8);
                System.out.println(body);
            }
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.out);
        }
    }

    public static class HelloServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setCharacterEncoding("utf-8");
            resp.setContentType("text/plain");
            resp.getWriter().printf("Hello from %s%n", this.getClass().getName());
        }

        /*
        @Override
        protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            // build allow here.
            // see HttpServlet.doOptions(HttpServletRequest, HttpServletResponse) for example
            String allow = "GET, PUT, OPTIONS";
            resp.setHeader("Allow", allow);
        }
         */
    }
}

您可以随时将HttpServlet.doOptions方法复制/粘贴到自己的servlet中,然后删除或更改TRACE行为默认值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接