설명
스택 트레이스 노출은 예외 발생 시 내부 스택 트레이스를 클라이언트 응답에 그대로 전달하는 취약점입니다. 스택 트레이스에는 클래스/패키지 구조, 파일 경로, 메서드 호출 흐름, 프레임워크·라이브러리 버전 단서 등이 포함되어 공격자에게 시스템 내부 정보를 제공합니다. 공격자는 의도적으로 예외를 유발(특정 파라미터 입력, 경계값 입력 등)해 스택 트레이스를 수집하고, 그 정보를 바탕으로 다른 취약점(SQL Injection, Path Traversal 등)을 더 쉽게 탐지·악용할 수 있습니다.
잠재적 영향
- 내부 구조 정보 노출: 클래스/패키지, 호출 경로, 파일 시스템 경로 등 내부 구현 세부정보가 드러나 방어 우회 단서를 제공합니다.
- 프레임워크/버전 식별: 사용 중인 라이브러리와 버전 추정이 가능해 알려진 취약점(known CVE) 공략이 쉬워집니다.
- 추가 취약점 탐색 가속: 오류 지점과 입력 흐름을 파악해 SQLi, RCE, 경로 조작 등의 정밀한 공격 시나리오 수립을 돕습니다.
- 환경 설정 유출: 운영/개발 환경 차이, 호스트명, 일부 설정 값이 메시지에 섞여 노출될 수 있습니다.
해결 방법
- 클라이언트에는 일반화된 오류 메시지만 반환: "요청을 처리할 수 없습니다" 등. 상세 스택 트레이스는 절대 응답에 포함하지 않습니다.
- 서버 측 로깅 일원화: logger.error("메시지", ex)로 서버 로그에만 기록하고, 접근 제어·보관 정책을 적용합니다.
- 공통 예외 처리 계층 사용: Servlet Filter, Spring @ControllerAdvice, JAX-RS ExceptionMapper 등으로 전역 예외를 잡아 표준화된 에러 응답을 보냅니다.
- 환경별 설정 강화: 운영 환경에서 상세 오류 비활성화(예: Spring Boot server.error.include-stacktrace=never), 커스텀 에러 페이지 구성.
- 사용자 메시지 검토: 클래스명, 경로, SQL, 구성 값 등 내부 식별자가 포함되지 않도록 검토합니다.
예시
변경 전
java
import javax.servlet.http.*;
import java.io.IOException;
public class ErrorEchoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String v = req.getParameter("div");
if (v != null) {
int n = Integer.parseInt(v);
int r = 100 / n; // 0 입력 시 예외 발생 가능
resp.getWriter().println("result=" + r);
}
} catch (Exception ex) {
// BAD: 스택 트레이스를 사용자 응답에 직접 출력
ex.printStackTrace(resp.getWriter());
}
}
}
변경 후
java
import javax.servlet.http.*;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.*;
public class ErrorSafeServlet extends HttpServlet {
private static final Logger LOG = Logger.getLogger(ErrorSafeServlet.class.getName());
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String v = req.getParameter("div");
if (v != null) {
int n = Integer.parseInt(v);
int r = 100 / n;
resp.getWriter().println("ok");
return;
}
resp.getWriter().println("ok");
} catch (Exception ex) {
String errorId = UUID.randomUUID().toString();
// 서버 로그에만 상세 스택 트레이스 기록
LOG.log(Level.SEVERE, "Unhandled exception, errorId=" + errorId, ex);
// 사용자에게는 일반화된 오류 메시지 전송
resp.resetBuffer();
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "요청 처리 중 오류가 발생했습니다. (id=" + errorId + ")");
}
}
}
설명:
- 변경 전: 예외가 발생하면 ex.printStackTrace(resp.getWriter())를 통해 스택 트레이스가 HTTP 응답 본문에 포함되어 전송됩니다. 이로 인해 클래스/패키지명, 파일 경로, 호출 흐름 등 내부 정보가 외부에 노출되어 공격자가 시스템을 정밀 분석할 수 있습니다.
- 변경 후: 서버 측 logger로만 스택 트레이스를 기록하고, 클라이언트에는 일반화된 메시지와 의미 없는 correlation ID만 전달합니다. 이로써 내부 구현 세부정보 노출을 방지하면서도 운영 측에서는 로그로 문제 원인을 추적할 수 있습니다.