All Implemented Interfaces:
Handler, org.eclipse.jetty.server.handler.gzip.GzipFactory, HandlerContainer, Container, Destroyable, Dumpable, LifeCycle

public class HostCheckHandlerextends org.eclipse.jetty.server.handler.gzip.GzipHandler
Block certain Host headers to prevent DNS rebinding attacks.

This Handler wraps the ContextHandlerCollection, which handles all the webapps (not just routerconsole). Therefore, this protects all the webapps.

  • Field Details

    • _context

      private final I2PAppContext _context
      The context reading the settings.
    • _portMapper

      private final PortMapper _portMapper
      Gives the console's configured addresses.
    • _listenHosts

      private final Set<String> _listenHosts
      Host names the console answers on.
    • PROP_REDIRECT

      private static final String PROP_REDIRECT
      Property asking for HTTP requests to be redirected.
      See Also:
    • PROP_GZIP

      private static final String PROP_GZIP
      Property enabling response compression.
      See Also:
  • Constructor Details

    • HostCheckHandler

      public HostCheckHandler(I2PAppContext ctx)
      MUST call setListenHosts(Set) afterwards.
      Parameters:
      ctx - the context used to resolve console ports, properties and logging
  • Method Details

    • setListenHosts

      public void setListenHosts(Set<String> hosts)
      Set the legal hosts. Not synched. Call this BEFORE starting. If empty, all are allowed.
      Parameters:
      hosts - contains hostnames or IPs. But we allow all IPs anyway.
    • handle

      public void handle(String pathInContext, Request baseRequest, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
      Block by Host header, redirect HTTP to HTTPS, pass everything else to the delegate.
      Specified by:
      handle in interface Handler
      Overrides:
      handle in class org.eclipse.jetty.server.handler.gzip.GzipHandler
      Throws:
      IOException
      ServletException
    • allowHost

      private boolean allowHost(String host)
      Should we allow a request with this Host header?

      ref: https://en.wikipedia.org/wiki/DNS_rebinding

      Parameters:
      host - the HTTP Host header, null ok
      Returns:
      true if OK
    • getHost

      private static String getHost(String host)
      Strip [] and port from a host header.
      Parameters:
      host - the HTTP Host header non-null
      Returns:
      the host with IPv6 brackets and any port removed
    • allowOrigin

      private boolean allowOrigin(HttpServletRequest request)
      Validate Origin header for POST requests. Allows requests with matching Origin (same-origin), or no Origin header. Rejects cross-origin POST requests to prevent CSRF attacks.
      Parameters:
      request - the HTTP request
      Returns:
      true if allowed
    • originAllowed

      static boolean originAllowed(String origin, String hostHeader, boolean secure)
      Decide whether an Origin header is acceptable for a request to the given Host header. Same-origin is allowed, as is a request where both sides are loopback and the ports match; anything else is rejected.

      Both sides are compared as literal text. Neither header is ever resolved through DNS: a hostname whose A record points at 127.0.0.1 (DNS rebinding) is not loopback here, which is what keeps the localhost allowance from becoming a CSRF bypass. Resolving attacker-supplied host names on the request thread would also hand a slow or hostile resolver control of the Jetty thread pool.

      Parameters:
      origin - the Origin header value, null or empty for a same-origin form POST that carries no Origin
      hostHeader - the Host header value, "host" or "host:port", IPv6 addresses bracketed
      secure - true when the request arrived over TLS, used only for the default port when the Host header omits one
      Returns:
      true if the request is allowed
    • isLoopbackName

      static boolean isLoopbackName(String host)
      True when the host is a textual loopback name: an IPv4 address in 127/8, the IPv6 loopback in its written forms, or "localhost". No DNS lookup is performed, so a hostname that happens to resolve to a loopback address is not accepted.
      Parameters:
      host - the host to test, null-safe
      Returns:
      true if the name itself denotes the local host
    • extractHost

      private static String extractHost(String hostPort)
      Extract host from "host:port" or "[ipv6]:port".
      Parameters:
      hostPort - the header value to split
      Returns:
      the host without brackets, or the input itself when no port is present
    • extractPort

      private static int extractPort(String hostPort, int defaultPort)
      Extract port from "host:port" or "[ipv6]:port", default to defaultPort if not found.
      Parameters:
      hostPort - the header value to split
      defaultPort - the port to assume when the value carries none
      Returns:
      the port, or -1 if a port was present but is not a number, so a malformed header fails the comparison instead of throwing
    • parsePort

      private static int parsePort(String s)
      Parse a port written in a header value.
      Parameters:
      s - the digits after the colon
      Returns:
      the port, or -1 if it is not a valid port number
    • sendRedirect

      private static void sendRedirect(int httpsPort, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
      Redirect to HTTPS.
      Parameters:
      httpsPort - the port to redirect the browser to
      httpRequest - the request whose server name, URI and query string form the target
      httpResponse - the response given the 307 status and Location header
      Throws:
      IOException - if the response output stream cannot be opened