1.7 HTTP 프로토콜 유효성 검증

개요

HTTP(HyperText Transfer Protocol)는 웹 통신의 기반이 되는 프로토콜입니다. 클라이언트(웹 브라우저 등)와 서버는 HTTP 메시지(요청/응답)를 주고받으며 통신하는데, 이 메시지의 구조나 내용이 표준 프로토콜 사양을 따르지 않거나 예상치 못한 값을 포함하는 경우 보안 문제가 발생할 수 있습니다.

예를 들어, HTTP 요청 헤더에 개행 문자(CRLF)를 삽입하여 응답 메시지를 분할하는 HTTP 응답 분할(HTTP Response Splitting) 공격이나, 비정상적인 `Content-Length` 또는 `Transfer-Encoding` 헤더를 사용하여 프록시 서버와 백엔드 서버 간의 요청 해석 차이를 악용하는 HTTP 요청 스머글링(HTTP Request Smuggling) 등이 대표적인 예입니다. 또한, 허용되지 않은 HTTP 메서드(TRACE 등)를 사용한 공격도 가능합니다.

이 기준은 수신되는 HTTP 요청 메시지의 메서드, 헤더, 파라미터 등 구성 요소의 유효성을 검증하고, HTTP 응답을 생성할 때 외부 입력값이 헤더 구조를 변경하지 못하도록 하는 보안 요구사항을 정의합니다.

보안 대책

  • - HTTP 메서드 검증: 각 URL(엔드포인트)에서 허용하는 HTTP 메서드(GET, POST, PUT, DELETE 등) 목록(Whitelist)을 정의하고, 허용되지 않은 메서드 요청은 거부합니다. 특히 TRACE, TRACK, OPTIONS 등 불필요한 메서드는 비활성화합니다.
  • - HTTP 헤더 검증: 애플리케이션 로직에서 사용하는 헤더 값(예: `Host`, `Content-Type`, `Content-Length`)의 유효성을 검증합니다. 비정상적인 헤더 형식이나 값을 탐지하고 처리합니다.
  • - 개행 문자(CRLF) 필터링: HTTP 응답 헤더 값으로 사용될 수 있는 모든 외부 입력값에서 개행 문자(`\r`, `\n`)를 제거하거나 적절히 인코딩하여 HTTP 응답 분할 공격을 방지합니다.
  • - 요청 파라미터 검증: URL 쿼리 스트링이나 요청 본문(Body)에 포함된 파라미터의 이름, 값, 개수, 순서 등의 유효성을 검증합니다. (다른 입력값 검증 기준과 연계)
  • - 안전한 웹 서버/프레임워크 사용 및 설정: HTTP 요청 스머글링 등 프로토콜 레벨 공격에 대한 방어 기능이 내장된 최신 버전의 웹 서버(Apache, Nginx 등), WAS(Tomcat 등), 웹 프레임워크를 사용하고, 관련 보안 설정을 활성화합니다. (예: `Transfer-Encoding` 헤더 처리 방식, 요청 크기 제한 등)
  • - 웹 방화벽(WAF) 활용: 웹 방화벽을 통해 비정상적인 HTTP 요청 패턴을 탐지하고 차단하는 규칙을 적용합니다.

코드 예시 (Java - HTTP 응답 분할 방지)

취약한 코드:

import javax.servlet.http.HttpServletResponse;

// 사용자 입력(redirectUrl)을 검증 없이 응답 헤더 값으로 사용
String redirectUrl = request.getParameter("url"); 
// 예: redirectUrl = "/index.jsp%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0a..."
response.sendRedirect(redirectUrl); // HTTP 응답 분할 공격에 취약할 수 있음 (Servlet 컨테이너 버전에 따라 다름)

// 직접 헤더 설정 시에도 취약
// String customHeaderValue = request.getParameter("input"); // %0d%0a 포함 가능
// response.setHeader("Custom-Header", customHeaderValue); 
                        
안전한 코드 (개행 문자 제거):

import javax.servlet.http.HttpServletResponse;

// 사용자 입력값에서 개행 문자 제거
String redirectUrl = request.getParameter("url"); 
if (redirectUrl != null) {
    // CRLF (\r\n) 및 LF (\n) 제거
    redirectUrl = redirectUrl.replaceAll("\\r|\\n", ""); 
}

// 검증된 URL만 사용 (Whitelist 방식 권장 - standards-input-webservice.html 참조)
if (isValidRedirectUrl(redirectUrl)) { // URL 유효성 검증 함수 (별도 구현 필요)
    response.sendRedirect(redirectUrl); // HTTP 응답 분할 방어됨
} else {
    // 유효하지 않은 URL 처리
    response.sendRedirect("/default-page.jsp");
}

// 직접 헤더 설정 시에도 개행 문자 제거
// String customHeaderValue = request.getParameter("input");
// if (customHeaderValue != null) {
//     customHeaderValue = customHeaderValue.replaceAll("\\r|\\n", "");
// }
// response.setHeader("Custom-Header", customHeaderValue);
                        

Python(Flask/Django 응답 객체 사용 시 프레임워크가 처리), JavaScript(Node.js의 `res.setHeader`), C#, PHP 등에서도 응답 헤더에 사용자 입력값을 사용할 때는 반드시 개행 문자를 제거하거나 적절히 인코딩해야 합니다.