如何使用 Jetty 嵌入服务器在 Spring Cloud Zuul 过滤器中检索 HTTP2 框架设置?

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

背景

我目前正在从事一个项目,该项目研究各种 HTTP 被动指纹识别技术以确保安全。我计划对请求进行指纹识别的某些方面包括客户端问候、header 顺序、HTTP2 框架设置和 HTTP2 伪 header 顺序。到目前为止,我已经实现了一个解决方案,通过扩展 Jetty 的 org.eclipse.jetty.util.ssl.SslContextFactory.Server class 来包装 SSLEngine 实例,从客户端 hello 中检索密码套件、压缩方法和扩展。然后我可以在 Zuul 过滤器中访问客户端问候数据,如下所示:

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;
}

有关 HTTP2 指纹识别的更多信息:

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

问题

虽然 Spring、Netflix Zuul 和 Jetty 嵌入式服务器有详尽的文档,但我一直无法找到一种方法来实现类似的检索 HTTP2 框架设置的解决方案。

您可以扩展 HTTP2ServerConnectionFactory 并将 protected ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint) 覆盖为 return 您的自定义实现(可以委托给原始实现)。

通过这种方式,您可以访问低级 HTTP/2 帧(作为对象,而不是字节格式),这可能允许您对客户端进行指纹识别。

用于详细说明 @sbordet 答案的最小代码示例。

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;
    }

}