本节书摘来异步社区《java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.7节,作者:【美】fred long(弗雷德•朗), dhruv mohindra(德鲁•莫欣达), robert c.seacord(罗伯特 c.西科德), dean f.sutherland(迪恩 f.萨瑟兰), david svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。
当有不可信的输入注入动态构造的代码中时,会引起代码注入攻击。一个明显的潜在漏洞是在java代码中使用javascript代码。javax.script包定义了java脚本引擎的接口和类,以及在java代码中使用这些接口和类的框架。javax.script api的滥用,会导致攻击者在目标系统上执行任意代码。
这条指南是《the cert® oracle® secure coding standard for java™》[long 2012]的“ids00-j. sanitize untrusted data passed across a trust boundary”的一个实例。
下面的违规代码示例将不可信的用户输入嵌入了负责打印输入的javascript语句中。
dummy');
var bw = new javaimporter(java.io.bufferedwriter);
var fw = new javaimporter(java.io.filewriter);
with(fw) with(bw) {
bwr = new bufferedwriter(new filewriter("config.cfg"));
bwr.write("some text"); bwr.close();
}
// ;<code>`</code>
这个示例中的脚本首先打印“dummy”,然后将“some text”写入一个名为config.cfg的配置文件中。这是一个可以导致任意代码执行的真实漏洞。
最好的防御代码注入漏洞的方式就是阻止包含可执行代码的用户输入。任何用于动态代码的用户输入,都必须进行无害化处理,例如,确保用户输入只包含白名单里的有效字符。最好是在数据输入后,通过使用用于存储和处理数据的提取方法,立即执行数据无害化处理。更多细节请参考“ids00-j. sanitize untrusted data passed across a trust boundary”[long 2012]。如果用户名中必须包含某些特殊字符,那么必须先对它们进行标准化,然后再对它们进行表单输入验证处理。下面的合规解决方案使用了白名单来防止脚本引擎对未经处理的输入进行解析。
class acc {
private static class restrictedaccesscontrolcontext {
private static final accesscontrolcontext instance;
static {
instance =
new accesscontrolcontext(
new protectiondomain[] {
new protectiondomain(null, null) // no permissions
});
}
}
private static void evalscript(final string firstname)
throws scriptexception {
scriptenginemanager manager = new scriptenginemanager();
final scriptengine engine =
manager.getenginebyname("javascript");
// restrict permission using the two-argument
// form of doprivileged()
try {
accesscontroller.doprivileged(
new privilegedexceptionaction
public object run() throws scriptexception {
engine.eval("print('" + firstname + "')");
return null;
}
},
// from nested class
restrictedaccesscontrolcontext.instance);
} catch (privilegedactionexception pae) {
// handle error
}
}<code>`</code>
将这个方法和白名单结合在一起使用,可以获得更高的安全性。
未能防止代码注入可能导致任意代码的执行。