Uncontrolled external site redirect - Host Header Injection - Java

Uncontrolled external site redirect - Host Header Injection - Java

Need

Prevention of unauthorized external site redirection

Context

  • Usage of Java 8 for developing applications with enhanced features and performance
  • Usage of javax.servlet-api for developing Java web applications with servlets

Description

Non compliant code

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

public class RedirectServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String host = request.getHeader("Host");
        String location = "http://" + host + "/redirect";
        response.sendRedirect(location);
    }
}
        
        

The above Java code represents a servlet that processes HTTP GET requests. When a GET request is received, the servlet retrieves the value of the "Host" header from the request, constructs a new URL using this value, and then redirects the client to this new URL.

This code is vulnerable to Host Header Injection attacks. In this attack, an attacker can manipulate the "Host" header in the HTTP request and cause the server to redirect the client to an arbitrary location. This could be used for phishing attacks, where the attacker redirects the user to a malicious site that looks like the original site but is designed to steal the user's credentials or other sensitive information.

The vulnerability lies in the line String host = request.getHeader("Host");. The servlet blindly trusts the "Host" header from the client's request and uses it to construct the redirect URL. Since HTTP headers can be easily manipulated by an attacker, this creates a security risk.

The server should not rely on the "Host" header to construct redirect URLs. Instead, it should use a known, trusted base URL. This would prevent an attacker from being able to manipulate the redirect location by modifying the "Host" header in their request.

Steps

  • Validate and sanitize the input: Before using the value of the 'Host' header, validate and sanitize it to ensure it contains a valid and trusted value. This can be done by checking if the value matches a whitelist of allowed hosts or by using a regular expression to validate the format.
  • Use a predefined list of allowed hosts: Instead of using the 'Host' header directly, maintain a predefined list of allowed hosts and compare the value of the 'Host' header against this list. Only redirect if the value matches one of the allowed hosts.
  • Use a secure redirect mechanism: Instead of using a simple redirect, consider using a more secure redirect mechanism that does not rely on the 'Host' header. For example, you can use a server-side redirect or a redirect based on a predefined URL.
  • Implement strict input validation: Implement strict input validation throughout the application to prevent other types of injection attacks. This includes validating and sanitizing all user-supplied input, not just the 'Host' header.

Compliant code

        import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

public class RedirectServlet extends HttpServlet {

    private static final List ALLOWED_HOSTS = Arrays.asList("www.example.com", "www.example2.com");

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String host = request.getHeader("Host");

        if (ALLOWED_HOSTS.contains(host)) {
            String location = "http://" + host + "/redirect";
            response.sendRedirect(location);
        } else {
            // Handle the case where the host is not allowed
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
        }
    }
}
        
        

The updated code includes a predefined list of allowed hosts ( ALLOWED_HOSTS). When a GET request is received, the Host header is extracted from the request. Instead of directly using this value for redirection, it is first checked against the ALLOWED_HOSTS list.

If the Host header value is found in the ALLOWED_HOSTS list, the server proceeds with the redirection as before. This ensures that the redirection only occurs for trusted hosts, preventing Host Header Injection attacks.

If the Host header value is not found in the ALLOWED_HOSTS list, the server responds with a 403 Forbidden error. This is done using the response.sendError(HttpServletResponse.SC_FORBIDDEN) method. This response indicates to the client that the server understood the request but refuses to authorize it. This is a secure way to handle requests from untrusted sources.

This solution effectively mitigates the risk of Uncontrolled External Site Redirect and Host Header Injection vulnerabilities by implementing strict input validation and using a secure redirect mechanism.

References