RequestDispatcher를 통한 파일 노출

File Disclosure via RequestDispatcher

설명

사용자 입력을 직접 getRequestDispatcher()에 전달하면 파일 노출(File Disclosure) 취약점이 발생할 수 있습니다.

ServletRequest.getRequestDispatcher()로 얻은 디스패처는 현재 웹 애플리케이션 컨텍스트의 정적 또는 동적 리소스로 요청을 전달하거나 그 출력을 포함합니다. 직접 HTTP 접근이 막힌 내부 리소스로도 전달될 수 있으므로, 사용자 입력이 경로를 결정하지 않게 해야 합니다. 모든 파일이 원본 그대로 반환되거나 컨텍스트 밖의 파일에 접근할 수 있다는 뜻은 아닙니다.

잠재적 영향

  • 정보 유출: 내부 리소스나 처리 결과가 권한 없는 사용자에게 노출될 수 있습니다.
  • 접근 제한 우회: 디스패치 대상과 적용되는 보안 제어에 따라 의도하지 않은 내부 기능에 접근할 수 있습니다. 경로 선택만으로 임의의 Java 코드 실행을 단정할 수는 없습니다.

해결 방법

  • RequestDispatcher에 사용자 입력을 직접 전달하지 않도록 합니다.
  • 가능할 경우 하드코딩된 안전한 경로만 사용하도록 합니다.
  • Lookup Table을 사용하여 허용된 경로만 접근 가능하도록 제한해야 합니다.
  • HTTP 리디렉션이 필요한 경우 서버가 정한 목적지만 사용하세요. sendRedirect() 자체는 입력 검증이나 인가 검사를 대신하지 않습니다.

예시

서블릿 등록과 인증 설정을 생략한 발췌입니다. 변경 후는 루트 컨텍스트에 배포한 애플리케이션의 고정 페이지로 리디렉션합니다. 다른 컨텍스트 경로에서는 목적지를 맞추고, 프로필 페이지에서도 사용자별 접근 권한을 검사하세요.

변경 전

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"); // 사용자 입력 사용
        RequestDispatcher dispatcher = request.getRequestDispatcher(filePath);
        dispatcher.forward(request, response); // 취약한 코드
    }
}

변경 후

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");

        // Lookup Table을 사용하여 허용된 페이지만 접근 가능하도록 설정
        String safePath = allowedPaths.getOrDefault(userInput, "/home.jsp");

        response.sendRedirect(safePath); // 안전한 리디렉션
    }
}

설명:

  • 변경 전: request.getParameter("file")을 직접 RequestDispatcher에 전달하여 공격자가 내부 파일을 포함하거나 전달할 수 있습니다.
  • 변경 후: 고정된 매핑에서 경로를 선택하여 브라우저에 새 요청을 하도록 안내합니다. 원래 요청을 내부에서 포워딩하는 동작과는 다릅니다.

관련 CVE

참조