There's a few moving parts ... disable TRACE from working, and remove TRACE from the OPTIONS Allow
response header field.
First, lets tackle the easy one, disabling TRACE from working.
That's done with a Constraint mapping at the ServletContext level.
That will impact all usage of TRACE across all Servlet endpoints.
See below example.
Next, removing TRACE from the OPTIONS Allow
header field.
That's not as easy to do in a global way like the disabling of TRACE.
This is because every HttpServlet
has a default implementation of doOptions(HttpServletRequest, HttpServletResponse)
which will always add TRACE and OPTIONS to the Allow
header, along with any other method that you have specifically enabled for that specific HttpServlet
endpoint.
You can see the actual default implementation of HttpServlet.doOption
here ...
https://github.com/eclipse-ee4j/servlet-api/blob/4.0.4-RELEASE/api/src/main/java/javax/servlet/http/HttpServlet.java#L361-L432
Why is TRACE and OPTIONS always returned on a OPTIONS method request?
That's because the default implementation of those methods in HttpServlet
always return a response, so they are always added to the Allow
header.
That means OPTIONS is controlled on a per-servlet scope.
You can override in your specific servlet the doOptions
method and make it return what you want, but you cannot have a top level Constraint or Filter control it, as the neither the Constraint nor the Filter know the Servlet endpoint details to properly determine the other parts of the OPTIONS Allow
header (like GET
, HEAD
, POST
, PUT
, etc...)
Here's some sample code to show the proper Constraint mapping and optional doOption override.
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);
}
*/
}
}
You can always copy/paste the HttpServlet.doOptions
method into your own servlet and just remove or alter the TRACE behavior default.