我在Deitel和Deitel的书中读到了这个错误预防技巧(3.1), Java:如何编程。我不明白这意味着什么。我只知道它正在讨论下面的代码行:
// display the name stored in object myAccount
System.out.printf("Name in object myAccount is:%n%s%n", myAccount.getName());
该段如下:
永远不要将格式控件用作从用户输入的字符串。当方法
System.out.printf
在其第一个参数中计算格式控制字符串时,该方法根据该字符串中的转换说明符执行任务。如果格式控制字符串是从用户获得的,则恶意用户可以提供由System.out.printf
执行的转换说明符,可能导致安全漏洞。
答案 0 :(得分:3)
严格地说,使用用户输入的字符串进行格式控制将是这样的:
String format=getFromUser(...);
System.out.printf(format, arg1, arg2, arg3...);
这可能是非常有害的,因为除了@JohnKugelman建议的编码注入之外,我可以想到四种简单方法,其中恶意用户可以通过format string破坏安全性:
WrongFormatConversion
:@Test public void wrongMask() { String s="january"; System.out.printf("%)/$#", s); }
MissingFormatArgumentException
:@Test public void highArgumentIndex() { String s="january"; System.out.printf("%1000$s%n", s); }
@Test public void highFieldWidth() { String s="january"; System.out.printf("%1000000s%n", s); }
@Test public void highArgumentWidth() { int n=12; System.out.printf("%01000000d%n", n); }
此测试推迟了几乎一分钟才能在我的计算机上执行。尝试使用足够高的宽度,并且在延迟几分钟后你可能会得到OutOfMemoryError
。
(通过输入字符串的简单预处理可以轻松避免代码注入以覆盖所有双引号,但这些缺陷属于语义性,因此它们更难找到并避免)
但这些都不适用于您的行,因为您的格式字符串是硬编码的,而不是来自用户的输入。
要了解从最终用户接受控制格式字符串的危险程度,您最好重复上面的测试集,但是在交互模式下:
public static void main(String[] args) { String format=args[0]; int n=12; System.out.printf(format, n); }
然后,想象自己是一个试图打破这个程序的黑客。使用这些建议值执行它:
%)/$#
%1000$d
%1000000d
%01000000d
结论:格式字符串必须由程序员组成,而不是由最终的交互式用户组成。