Description
Using untrusted input as the format string in String.format, System.out.printf, or Formatter.format lets an attacker insert format specifiers such as %s, %d, or positional references such as %1$.... These can cause argument-count or type errors, or expose other arguments supplied to the same call in an unintended way.
Potential impact
- Service errors:
MissingFormatArgumentExceptionorIllegalFormatConversionExceptioncan interrupt request processing. - Information disclosure: positional specifiers such as
%1$...and%2$...can expose other arguments supplied to the call. - Log corruption:
%ncan introduce new lines and disrupt log structure or monitoring. - Resource exhaustion: extremely large widths or precision, such as
%1000000s, may consume excessive memory or CPU.
Remediation
- Keep format strings constant and pass user input only as argument values.
- To print input as text, use the fixed format string
"%s". - If users must select a pattern, permit only a small allow-list of patterns and use a default for other values.
- Use parameterized logging, such as SLF4J
logger.info("User: {}", user). Handle newlines and other log control characters separately.
Examples
Before
java
import javax.servlet.http.HttpServletRequest;
public class BadFormatExample {
public void printUser(HttpServletRequest req) {
String pattern = req.getParameter("pattern"); // 외부 입력
String user = req.getParameter("user");
// BAD: 포맷 문자열에 외부 입력 사용
System.out.printf(pattern, user);
}
}
After
java
import javax.servlet.http.HttpServletRequest;
import java.util.Set;
public class GoodFormatExample {
private static final Set<String> ALLOWED_PATTERNS = Set.of("User: %s", "Hello, %s!");
public void printUser(HttpServletRequest req) {
String pattern = req.getParameter("pattern");
String user = req.getParameter("user");
// 기본은 고정 포맷 사용
String safePattern = "User: %s";
// 포맷을 외부에서 받아야 한다면 허용 목록으로 제한
if (pattern != null && ALLOWED_PATTERNS.contains(pattern)) {
safePattern = pattern;
}
System.out.printf(safePattern, user);
}
public void echoRaw(HttpServletRequest req) {
String text = req.getParameter("text");
// 입력을 그대로 보여주려면 포맷 문자열은 항상 고정("%s")
System.out.printf("%s", text);
}
}
Explanation:
- Before: External input becomes the format string. This call supplies only one argument, so
%2$scauses a missing-argument exception, while%ddoes not match the string argument's type. Positional specifiers do not read arbitrary memory that was not passed to the call. - After: The format is fixed or selected from a limited allow-list. Input is passed as an argument, so format specifiers within it are not interpreted. Apply any output-context encoding and log control-character handling separately.