설명
Spring MVC의 ModelAndView에 지정한 뷰 이름은 구성된 ViewResolver를 통해 실제 뷰로 해석됩니다. 항상 JSP 파일로 매핑되는 것은 아닙니다. 검증되지 않은 입력이 뷰 이름을 결정하면, 구성에 따라 의도하지 않은 내부 뷰나 리디렉션·포워딩 대상을 선택할 수 있습니다.
잠재적 영향
- 정보 유출: 접근 권한이 확인되지 않은 뷰에서 민감한 데이터가 노출될 수 있습니다.
- 의도하지 않은 처리: 선택 가능한 뷰와 리졸버 구성에 따라 내부 처리나 외부 이동이 유발될 수 있습니다. 뷰 이름 제어만으로 임의의 Java 코드가 실행된다고 단정할 수는 없습니다.
해결 방법
- ModelAndView 사용자 입력을 직접 전달하지 않도록 합니다.
- 가능할 경우 하드코딩된 안전한 경로만 사용하도록 합니다.
- Lookup Table을 사용하여 허용된 경로만 접근 가능하도록 제한해야 합니다.
- 리디렉션이 필요한 경우에도 서버가 정한 목적지만 사용하고 목적지의 접근 권한을 확인하세요.
sendRedirect()자체는 입력 검증을 대신하지 않습니다.
예시
뷰 리졸버와 인증 설정을 생략한 컨트롤러 발췌입니다. home과 profile은 애플리케이션에 정의된 예시 이름이며, 프로필 데이터는 별도로 사용자별 인가를 적용해야 합니다.
변경 전
java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
@Controller
public class UnsafeController {
@GetMapping("/view")
public ModelAndView unsafeView(HttpServletRequest request) {
String viewName = request.getParameter("page"); // 사용자 입력 사용
return new ModelAndView(viewName); // 취약한 코드
}
}
변경 후
java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Controller
public class SafeController {
private static final Map<String, String> allowedViews = new HashMap<>();
static {
allowedViews.put("home", "home");
allowedViews.put("profile", "profile");
}
@GetMapping("/view")
public ModelAndView safeView(@RequestParam(name = "page", required = false, defaultValue = "home") String page) {
// Lookup Table를 통한 whitelist 기반 검증
String safeView = allowedViews.getOrDefault(page, "home");
return new ModelAndView(safeView);
}
@GetMapping("/redirect")
public void safeRedirect(@RequestParam(name = "page", required = false, defaultValue = "home") String page,
HttpServletResponse response) throws IOException {
String redirectPath = allowedViews.getOrDefault(page, "/home");
response.sendRedirect(redirectPath); // 안전한 리디렉션
}
}
설명:
- 변경 전:
page입력을ModelAndView의 뷰 이름으로 직접 사용합니다. - 변경 후: 고정된 매핑에서 뷰 이름을 선택하고 알 수 없는 값은
home으로 처리합니다. 별도 리디렉션 메서드 역시 고정된 목적지를 사용하며, 상대 URL이 실제 배포 경로에서 올바르게 해석되는지 확인하세요.