설명
사용자 입력에 포함된 캐리지 리턴(\r, CR)이나 라인 피드(\n, LF)가 HTTP 응답 헤더에 그대로 전달되고 서버·프록시·수신자가 이를 메시지 경계로 해석하면 응답 분할이 발생할 수 있습니다. 추가 헤더나 응답을 주입하면 캐시 오염이나 XSS로 이어질 수 있습니다.
일부 서버는 잘못된 헤더 값을 거부하거나 정리하지만, 배포 환경 전체가 같은 방식으로 처리한다고 가정하지 마세요. 컨테이너의 헤더 검증을 유지하고 애플리케이션에서도 값의 문법을 확인하세요.
잠재적 영향
- HTTP 응답 조작: 공격자가 추가적인 HTTP 응답을 삽입하여 브라우저의 동작을 조작할 수 있습니다.
- 캐시 중독(Cache Poisoning): 악성 응답을 캐시에 저장하여 사용자에게 악성 콘텐츠를 제공할 수 있습니다.
- Cross-Site Scripting(XSS): 공격자가 HTTP 응답 본문을 변조하여 악성 스크립트를 실행할 수 있습니다.
해결 방법
- HTTP 응답 헤더에 사용자 입력을 직접 포함하지 않기
- 응답 헤더 값은 미리 정의된 값으로 제한해야 합니다.
- 입력값 검증 및 거부 처리
\r,\n및 기타 제어 문자는 응답 헤더 값에 포함되지 않도록 요청을 거부합니다.
- 허용 목록 기반 헤더 값 사용
- 헤더 값이 사용자 선택에 따라 달라져야 한다면 미리 정의한 값 집합에서만 선택하도록 제한합니다.
예시
변경 전
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class InsecureResponseSplitting {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input"); // 사용자 입력을 직접 헤더에 포함
response.setHeader("Custom-Header", userInput); // 취약한 코드
}
}
변경 후
이 사용자 정의 헤더는 최대 128자의 출력 가능한 ASCII만 허용합니다. 실제 헤더별 문법도 별도로 검증하세요.
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SecureResponseSplitting {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input");
if (!isValidHeaderValue(userInput)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid input");
return;
}
response.setHeader("Custom-Header", userInput); // 검증된 값만 헤더에 사용
}
private boolean isValidHeaderValue(String value) {
return value != null
&& value.length() <= 128
&& value.chars().allMatch(c -> c >= 0x20 && c <= 0x7e);
}
}
설명:
- 변경 전:
- 사용자 입력을
setHeader()에 직접 전달하여 수신 경로가 CR/LF를 메시지 경계로 허용하면 응답을 조작할 수 있음.
- 사용자 입력을
- 변경 후:
isValidHeaderValue()를 사용하여 CR/LF와 제어 문자를 거부.- Java 문자열 이스케이프를 HTTP 헤더 보안 대책으로 사용하지 않고, 검증된 값만
setHeader()에 전달.