1. 포맷 스트링 삽입의 위험성
CWE-134는 printf(), String.format()과 같은 포맷팅 함수를 사용할 때, 포맷 문자열(Format String) 인자에 외부 입력값을 직접 전달할 때 발생합니다. 공격자가 %x, %s, %n과 같은 포맷 지정자를 입력값에 주입하면, 의도하지 않은 메모리의 내용을 출력(정보 유출)하거나 특정 메모리 주소에 값을 기록(실행 흐름 조작)할 수 있습니다. 자바의 경우 C언어만큼 치명적인 메모리 쓰기 공격은 어렵지만, 여전히 민감한 정보 유출이나 비정상적인 애플리케이션 종료(DoS)를 유발할 수 있습니다.
2. 흔히 발생하는 취약한 패턴
가장 대표적인 사례는 로그를 남기거나 문자열을 서식화할 때 입력값을 포맷 인자 위치에 그대로 넣는 경우입니다.
• 위험한 코드: System.out.printf(userInput);
• 공격 예시: 사용자가 %x %x %x %x를 입력하면 시스템 내부의 해시값이나 메모리 주소 정보가 출력됩니다. %s를 반복 주입하면 잘못된 주소 참조로 인해 시스템 에러를 유발할 수도 있습니다.
3. 실무적 대응: 고정된 포맷 문자열 사용
방어의 핵심은 **"포맷 문자열은 개발자가 정의한 고정값이어야 한다"**는 원칙을 지키는 것입니다.
• 데이터와 서식의 분리: 사용자 입력값은 항상 포맷 문자열의 '인자'로 전달되어야 합니다.
• 로그 라이브러리 활용: SLF4J, Log4j 등은 logger.debug("User: {}", userInput);와 같이 파라미터 바인딩 방식을 사용하여 이 취약점을 원천 차단합니다.
• 입력값 필터링: 포맷 지정자 문자인 % 등이 포함되어 있는지 사전에 검증(CWE-112)합니다.
4. CWE-134 대응 및 안전한 문자열 포맷팅 자바 코드 예시
코멘트: 포맷 스트링 삽입 방어의 핵심은 "사용자의 입력이 시스템의 서식 규칙을 결정하게 두지 않는 것"입니다. 모든 출력 형식은 프로그램이 제어해야 하며, 외부 데이터는 오직 '표시될 값'으로만 존재해야 합니다. logger.debug()의 중괄호({}) 바인딩 습관을 들이는 것만으로도 대다수의 포맷 스트링 위협으로부터 시스템을 안전하게 지킬 수 있습니다.
댓글 달기