File disclosure through RequestDispatcher

File disclosure through RequestDispatcher

Description

Passing input directly to getRequestDispatcher() may expose internal resources. A dispatcher obtained from ServletRequest.getRequestDispatcher() forwards to a static or dynamic resource, or includes its output, within the current web application context. It can reach resources blocked from direct HTTP access. This does not mean every file is returned as raw bytes or that paths can escape the servlet context.

Potential impact

  • Information disclosure: Unauthorized users may receive internal resources or processing results.
  • Access-control bypass: Depending on the target and applicable controls, unintended internal functions may be reached. Path selection alone does not establish arbitrary Java code execution.

Remediation

  • Do not pass user input directly to RequestDispatcher.
  • Prefer fixed, application-controlled paths.
  • Map permitted choices to fixed paths with an allow-list.
  • If HTTP redirection is needed, use only server-defined destinations. sendRedirect() does not replace validation or authorization.

Examples

Servlet registration and authentication configuration are omitted. The after example redirects to fixed pages in an application deployed at the root context. Adapt destinations for other context paths, and enforce per-user authorization on the profile page.

Before

java
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.RequestDispatcher;

public class UnsafeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String filePath = request.getParameter("file"); // User-controlled input
        RequestDispatcher dispatcher = request.getRequestDispatcher(filePath);
        dispatcher.forward(request, response); // Unvalidated destination
    }
}

After

java
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class SafeServlet extends HttpServlet {
    private static final Map<String, String> allowedPaths = new HashMap<>();

    static {
        allowedPaths.put("home", "/home.jsp");
        allowedPaths.put("profile", "/profile.jsp");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String userInput = request.getParameter("page");

        // Select only a page in the fixed mapping.
        String safePath = allowedPaths.getOrDefault(userInput, "/home.jsp");

        response.sendRedirect(safePath); // Fixed redirect destination
    }
}

Explanation:

  • Before: Passes the value of request.getParameter("file") directly to the dispatcher, which may expose or invoke an unintended internal resource.
  • After: Chooses a path from a fixed mapping and tells the browser to make a new request. This differs from internally forwarding the original request.

Related CVEs

References