Search code examples
javaspring-bootembedded-jettynetflix-zuul

How do you retrieve HTTP2 frame settings in a Spring Cloud Zuul filter with Jetty embed server?


Background

I'm currently working on a project that looks at various HTTP passive fingerprinting techniques for the purpose of security. Some aspects of the request I plan to fingerprint include the client hello, header order, HTTP2 frame settings, and HTTP2 pseudo header order. So far I have implemented a solution to retrieve the cipher suites, compression methods, and extensions from the client hello by extending Jetty's org.eclipse.jetty.util.ssl.SslContextFactory.Server class to wrap SSLEngine instances. I can then access the client hello data in a Zuul filter as shown below:

private static final String SSL_SESSION_ATTRIBUTE = "org.eclipse.jetty.servlet.request.ssl_session";

@Override
public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest request = ctx.getRequest();

    SSLSession sslSession = (SSLSession) request.getAttribute(SSL_SESSION_ATTRIBUTE);
    ClientHello clientHello = (ClientHello) sslSession.getValue("client-hello");
        
    return null;
}

More information about HTTP2 fingerprinting:

http://webcache.googleusercontent.com/search?q=cache:https://www.akamai.com/us/en/multimedia/documents/white-paper/passive-fingerprinting-of-http2-clients-white-paper.pdf

Issue

Although Spring, Netflix Zuul, and Jetty embedded server have thorough documentation I have been unable to find a way to achieve a similar sort of solution for retrieving HTTP2 frame setting.


Solution

  • A minimal code example to elaborate on @sbordet's answer.

    You can extend HTTP2ServerConnectionFactory and override protected > ServerSessionListener newSessionListener(Connector connector, EndPoint > endPoint) to return your custom implementation (that may delegate to the original implementation).

    MyServerSessionListener.java

    public class MyServerSessionListener implements ServerSessionListener {
    
        private final ServerSessionListener delegate;
    
        public MyServerSessionListener(ServerSessionListener delegate) {
            this.delegate = delegate;
        }
    
        ...
    
        @Override
        public void onSettings(Session session, SettingsFrame settingsFrame) {
            Map<Integer, Integer> settings = settingsFrame.getSettings();
    
            RequestContext context = RequestContext.getCurrentContext();
            context.set("http2-frame-settings", settings);
    
            delegate.onSettings(session, settingsFrame);
        }
    
    }
    

    MyHTTP2ConnectionFactory.java

    public class MyHTTP2ServerConnectionFactory extends HTTP2ServerConnectionFactory {
    
        public MyHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration) {
            super(httpConfiguration);
        }
    
        public MyHTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration, String... protocols) {
            super(httpConfiguration, protocols);
        }
    
        @Override
        protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint) {
            ServerSessionListener delegate = super.newSessionListener(connector, endPoint);
            return new MyServerSessionListener(delegate);
        }
    
    }
    

    MyFilter.java

    public class MyFilter extends ZuulFilter {
    
        ...
    
        @Override
        public Object run() {
            RequestContext context = RequestContext.getCurrentContext();
    
            Map<Integer, Integer> http2FrameSettings = (Map<Integer, Integer>) context.get("http2-frame-settings");
    
            return null;
        }
    
    }