错误消息400:折叠页眉

时间:2018-08-08 13:26:19

标签: java jetty mode rfc2616

我们最近从Jetty v 6_1_26切换到9_4_11。

我们遵循以下网址: http://jetty.4.x6.nabble.com/Configuring-option-2-of-RFC-7230-paragraph-5-HTTP-header-folding-td4966330.html

并在我们的代码中进行了必要的更改,以将遵从模式设置为RFC2616,因为我们需要产品中的多行标头支持。

这是我们的设置方式:

JasperFillManager.fillReport(JasperReport jReport, Map<String,Object> params, Connection connection)

并且我们已使用以下代码成功验证了合规性模式正在更改。

public class JettyPsServerConnector extends ServerConnector {
  public JettyServerConnector(Server server, PsSelectorProvider psProvider,Map<String,String> configMap, boolean useSSL) throws Exception{

    super(server,0,-1, new SslConnectionFactory( getSSLContextFactory(configMap),HttpVersion.HTTP_1_1.asString()),
                           new HttpConnectionFactory( getHTTPConfiguration(), HttpCompliance.RFC2616 ));

    }

这将合规模式打印为“ RFC2616”。

但是即使将遵从模式设置为RFC2616,我们仍然会看到此问题。

Connector[] connectorArray = server.getConnectors();
for(Connector conn: connectorArray){
    Collection col = conn.getConnectionFactories();
    for(ConnectionFactory con: col){
        if (con instanceof HttpConnectionFactory)
            System.out.println("HTTP Compliance Mode:"+((HttpConnectionFactory)con).getHttpCompliance());
                    }
        }

我们正在通过两者之间的代理服务器访问服务器代码。

我们无法弄清楚是什么原因造成的。

1 个答案:

答案 0 :(得分:0)

首先,除非您完全(我是说100%)完全了解Jetty 9.x中的整个ServerConnectorHttpConnectionFactory行为,否则请不要从Endpoint开始扩展。一个小错误,您会打破很多事情。这并不是一个可扩展的公共API,在Jetty的未来版本中可能会标记为final。

如果需要自定义行为,请先查看HttpConfiguration.Customizer,然后如果仍然需要其他自定义,则改用自定义HttpConnectionFactory

接下来,知道HttpCompliance只是HttpComplianceSection设置的集合/集合的持有者。您可能要确保所选的HttpCompliance设置中没有包含HttpComplianceSection.NO_FIELD_FOLDING

最后,请确保您已确定并修复了那些有问题的客户端,由于放宽的行为(例如,行折叠)造成/造成的众多安全问题,近年来使用HTTP的趋势越来越严格。有一天,即使您的负载均衡器,代理,路由器等也将拒绝此类请求。

过时的RFC2616出于许多原因而进行了更新,其中很大一部分是使用诸如MUST NOT(在RFC2119 Section 2中明确定义的短语)专门指出某些危险行为(例如行折叠) ),使行为对于更新的规范而言不是可选的。 IETF在2013年弃用Header Field行折叠的原因是由于与Header注入漏洞的变化相关的许多安全问题。启用标头字段行折叠后,您将无法防止响应拆分,会话固定,跨站点脚本,安全来源检查和恶意重定向。

许多现代的防火墙/网关/路由器/负载平衡器不支持标题折叠。

还要注意,HTTP / 2不支持标头折叠。

如果(由于某些原因)无法纠正那些有问题的客户端,那么您唯一的选择就是不要从现在开始升级任何服务器软件,以延迟这一天的发生。 (您无法控制的其他中介人甚至可能在您到达之前使这些请求失败!)

  

必须解决客户端中标头折叠使用过时的问题,因为超出您控制范围的许多事情已经拒绝了该概念,您您剩下的唯一选择就是修复客户端行为。

无论如何,这是使用RFC2616遵从模式和行折叠的这种行为的独立演示。

package jetty;

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

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URI;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;

public class HttpComplianceDemo
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server();

        HttpConfiguration http_config = new HttpConfiguration();
        http_config.setSendServerVersion(true);

        HttpCompliance compliance = HttpCompliance.RFC2616;
        ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config, compliance));
        connector.setPort(9090);
        server.addConnector(connector);

        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        context.addServlet(DumpServlet.class, "/dump");
        context.addServlet(DefaultServlet.class, "/");

        HandlerList handlers = new HandlerList();
        handlers.addHandler(context);
        handlers.addHandler(new DefaultHandler());

        server.setHandler(handlers);

        try
        {
            server.start();

            testLineFolding(server.getURI().resolve("/"));
        }
        finally
        {
            server.stop();
        }
    }

    private static void testLineFolding(URI serverUri) throws IOException
    {
        String host = serverUri.getHost();
        int port = serverUri.getPort();

        try (Socket socket = new Socket(host, port);
             OutputStream out = socket.getOutputStream();
             InputStream in = socket.getInputStream())
        {
            StringBuilder rawRequest = new StringBuilder();
            rawRequest.append("GET /dump HTTP/1.1\r\n");
            rawRequest.append("Host: ").append(serverUri.getRawAuthority()).append("\r\n");
            rawRequest.append("Connection: close\r\n");
            rawRequest.append("X-Foo: name\r\n"); // the header with line folding
            rawRequest.append(" extra\r\n");
            rawRequest.append("\r\n");

            byte bufRequest[] = rawRequest.toString().getBytes(UTF_8);
            System.out.println("--request--");
            System.out.println(new String(bufRequest, UTF_8));
            out.write(bufRequest);
            out.flush();

            ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
            IO.copy(in, outBuf);
            String response = new String(outBuf.toByteArray(), UTF_8);
            System.out.println("--Response--");
            System.out.println(response);
        }
    }

    public static class DumpServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
        {
            resp.setContentType("text/plain");
            PrintWriter out = resp.getWriter();
            Enumeration<String> enNames = req.getHeaderNames();
            while (enNames.hasMoreElements())
            {
                String name = enNames.nextElement();
                String value = req.getHeader(name);
                out.printf("- [HEADER:%s=%s]\n", name, value);
            }
        }
    }
}

输出...

2018-08-08 11:21:27.811:INFO::main: Logging initialized @338ms to org.eclipse.jetty.util.log.StdErrLog
2018-08-08 11:21:27.946:INFO:oejs.Server:main: jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 9.0.4+11
2018-08-08 11:21:28.004:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@78aab498{/,null,AVAILABLE}
2018-08-08 11:21:28.204:INFO:oejs.AbstractConnector:main: Started ServerConnector@15ff3e9e{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2018-08-08 11:21:28.205:INFO:oejs.Server:main: Started @739ms
--request--
GET /dump HTTP/1.1
Host: 192.168.0.119:9090
Connection: close
X-Foo: name
 extra


--Response--
HTTP/1.1 200 OK
Connection: close
Date: Wed, 08 Aug 2018 16:21:28 GMT
Content-Type: text/plain;charset=iso-8859-1
Content-Length: 91
Server: Jetty(9.4.11.v20180605)

- [HEADER:Connection=close]
- [HEADER:X-Foo=name extra]
- [HEADER:Host=192.168.0.119:9090]

2018-08-08 11:21:28.307:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@15ff3e9e{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
2018-08-08 11:21:28.310:INFO:oejsh.ContextHandler:main: Stopped o.e.j.s.ServletContextHandler@78aab498{/,null,UNAVAILABLE}

如果将HttpCompliance模式更改为RFC7230,则会得到不同的结果。

--request--
GET /dump HTTP/1.1
Host: 192.168.0.119:9090
Connection: close
X-Foo: name
 extra


--Response--
HTTP/1.1 400 Header Folding
Content-Type: text/html;charset=iso-8859-1
Content-Length: 57
Connection: close
Server: Jetty(9.4.11.v20180605)

<h1>Bad Message 400</h1><pre>reason: Header Folding</pre>