포맷 문자열 취약점

Use of Externally-Controlled Format String

설명

포맷 문자열 취약점은 String.format, System.out.printf, Formatter.format 등에서 포맷 문자열 자리에 신뢰할 수 없는 외부 입력을 그대로 사용할 때 발생합니다. 공격자는 추가 포맷 지정자(예: %s, %d, 위치 지정자 %1$... 등)를 삽입하여 인자 개수/타입 불일치를 유도해 예외를 발생시키거나, 위치 지정자를 악용해 개발자가 의도하지 않은 다른 인자의 내용을 노출시키는 등의 공격을 할 수 있습니다.

잠재적 영향

  • 서비스 장애: 잘못된 포맷 지정으로 MissingFormatArgumentException, IllegalFormatConversionException 등이 발생해 요청 처리 실패 또는 장애 유발

  • 정보 노출: 위치 지정자(%1$..., %2$...) 등을 악용해 다른 인자의 값이 노출될 수 있음

  • 로그 오염/우회: %n 등 제어 문자를 삽입해 로그 포맷을 깨뜨리거나 다중 라인을 유도해 모니터링을 우회

  • 자원 고갈형 DoS: 매우 큰 폭/정밀도(%1000000s 등)를 사용해 과도한 메모리/CPU 사용을 유발할 가능성

해결 방법

  • 포맷 문자열은 항상 상수(고정 문자열)로 사용하고, 사용자 입력은 포맷 인자로만 전달하기

  • 입력을 그대로 출력해야 한다면 포맷 문자열로 "%s"를 사용하기

  • 포맷 패턴을 외부에서 받아야 한다면, 허용 목록(whitelist) 기반으로 제한된 소수 패턴만 허용하고 나머지는 기본 패턴으로 대체

  • 로그 출력 시 SLF4J의 매개변수 바인딩(예: logger.info("User: {}", user))처럼 안전한 API 사용 고려

취약한 코드 및 안전한 코드 예시

취약한 코드

안전한 코드

설명:

  • 취약한 코드: 외부 입력값이 포맷 문자열로 그대로 사용되었습니다. 공격자가 %2$s, %d 등 예기치 않은 포맷 지정자를 넣으면 인자 부족/타입 불일치로 예외가 발생하거나, 위치 지정자를 통해 다른 인자의 값이 노출될 수 있습니다.

  • 안전한 코드: 포맷 문자열을 상수로 고정하고, 필요한 경우에만 허용 목록에 든 안전한 패턴만 사용합니다. 사용자 입력은 항상 포맷 인자로만 전달하거나 "%s"를 사용해 원문 그대로 출력하므로 예외 유발 및 정보 노출을 방지합니다.

참조

Last updated