本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.10节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。
创建可变方法参数的防御性副本,可以减轻来自各种安全漏洞的威胁,更多信息请参考《The CERT® Oracle® Secure Coding Standard for Java™》[Long 2012]的“OBJ06-J. Defensively copy mutable inputs and mutable internal components”。然而,对clone()方法不当地使用,可以使攻击者利用这一漏洞,提供看上去正常的参数,但随后返回意想不到的结果。这样的对象可能因此绕过验证和安全检查。当这样一个类可能会作为一个参数传递给一个方法时,应当把这个参数视为不可信任的,同时不要使用该类提供的clone()方法。另外,不要使用未经final修饰的类的clone()方法来创建防御性副本。
该指南是指南15的一个特定实例。
下面的违规代码示例定义了一个validateValue()方法来验证时间值。
private Boolean validateValue(long time) { // Perform validation return true; // If the time is valid } private void storeDateInDB(java.util.Date date) throws SQLException { final java.util.Date copy = (java.util.Date)date.clone(); if (validateValue(copy.getTime())) { Connection con = DriverManager.getConnection( "jdbc:microsoft:sqlserver://<HOST>:1433", "<UID>", "<PWD>" ); PreparedStatement pstmt = con.prepareStatement("UPDATE ACCESSDB SET TIME = ?"); pstmt.setLong(1, copy.getTime()); // ... } }``` storeDateInDB()方法接受一个不可信的日期参数,尝试利用其clone()方法创建一个防御性副本。这就允许攻击者通过一个继承自Date的恶意日期类来取得程序的控制权。如果攻击者的代码运行特权和storeDateInDB()方法一样,那么他只需在clone()方法里嵌入恶意代码:class MaliciousDate extends java.util.Date { @Override public MaliciousDate clone() { // malicious code goes here }}`然而,如果攻击者只能提供恶意的日期参数,但是没有足够特权,他还是可以绕过验证,从而混淆程序的其余部分。试想一下这个例子:
public class MaliciousDate extends java.util.Date { private static int count = 0; @Override public long getTime() { java.util.Date d = new java.util.Date(); return (count++ == 1) ? d.getTime() : d.getTime() - 1000; } }``` getTime()方法第一次被调用时,这个恶意的日期看上去是一个正常的日期对象,这使得它绕过了storeDateInDB()里的验证方法。然而,实际存储在数据库中的时间却是不正确的。 ####合规解决方案 下面的合规解决方案避免了clone()方法的使用。取而代之,创建了一个新的java.util.Date对象,并在后续的代码中,使用该对象做访问控制检查和数据库插入。private void storeDateInDB(java.util.Date date) throws SQLException { final java.util.Date copy = new java.util.Date(date.getTime()); if (validateValue(copy.getTime())) { Connection con = DriverManager.getConnection( "jdbc:microsoft:sqlserver://:1433", "", "" ); PreparedStatement pstmt = con.prepareStatement("UPDATE ACCESSDB SET TIME = ?"); pstmt.setLong(1, copy.getTime()); // ... }}`
下面的违规代码示例展示了一个Java核心类AtomicReferenceArray的构造函数,它来自于Java 1.7.0版本的第2次更新。
public AtomicReferenceArray(E[] array) { // Visibility guaranteed by final field guarantees this.array = array.clone(); }``` 这段代码的调用导致漏洞被利用,在2012年4月影响了600 000台Macintosh电脑。1 ####合规解决方案(CVE-2012-0507) 在Java 1.7.0版本的第3次更新中,对上面提到的构造函数进行了修改,使用Arrays.copyOf()方法替换了clone()方法。public AtomicReferenceArray(E[] array) { // Visibility guaranteed by final field guarantees this.array = Arrays.copyOf( array, array.length, Object[].class);}`
使用clone()方法复制不可信的参数会给攻击者执行任意代码的机会。
相关资源:敏捷开发V1.0.pptx