java安全编码指南之:对象构建
文章目录
- 简介
- 构造函数的异常
- Finalizer Attack
- 解决Finalizer Attack
- 使用final class
- 使用final finalize方法
- 使用flag变量
- 使用this或者super
简介
程序员肯定是不缺对象的,因为随时都可以构建一个,对象多了肯定会出现点安全问题,一起来看看在java的对象构建中怎么保证对象的安全性吧。
构造函数的异常
考虑下面的一个例子:
public class SensitiveOperation {public SensitiveOperation(){if(!doSecurityCheck()){throw new SecurityException("Security check failed!");}}//Security check return falseprivate boolean doSecurityCheck(){return false;}public void storeMoney(){System.out.println("Store 1000000 RMB!");}
}
上面的例子中,我们在构造函数中做了一个securityCheck,因为这个securityCheck返回的值是false,所以会抛出SecurityException。
看下调用的例子:
public static void main(String[] args) {SensitiveOperation sensitiveOperation = new SensitiveOperation();sensitiveOperation.storeMoney();}
这个调用会抛出下面的异常:
Exception in thread "main" java.lang.SecurityException: Security check failed!at com.flydean.SensitiveOperation.<init>(SensitiveOperation.java:11)at com.flydean.SensitiveUsage.main(SensitiveUsage.java:10)
那么问题来了,上面的这个class是不是安全的呢?
Finalizer Attack
上面的class不是final的,所以我们可以构造一个class去继承它。然后考虑这样一个问题,当构造函数抛出异常之后,会执行什么操作呢?
如果该对象已经被构建了,那么这个对象在GC的时候需要执行finalize方法。那么我们是不是可以在finalize方法中绕过安全检查呢?
看下面的例子:
public class SensitiveOperationFinalizer extends SensitiveOperation{public SensitiveOperationFinalizer(){}@Overrideprotected void finalize() {System.out.println("We can still do store Money action!");this.storeMoney();System.exit(0);}
}
上的例子中,我们继承了SensitiveOperation,并且实现了finalize方法,在finalize中,我们调用了storeMoney。看下运行的代码:
public void testFinalizer() throws InterruptedException {try {SensitiveOperation sensitiveOperation = new SensitiveOperationFinalizer();sensitiveOperation.storeMoney();}catch (Exception e){System.out.println(e.getMessage());}System.gc();Thread.sleep(10000);}
运行结果:
Security check failed!
We can still do store Money action!
Store 1000000 RMB!
可以看到,虽然我们构造函数抛出了异常,但是storeMoney的操作还是被执行了!
这个操作就叫做Finalizer Attack。
解决Finalizer Attack
怎么解决这个构造函数抛出异常的问题呢?这里给大家介绍几种解决方法。
使用final class
如果使用final class,那么类是不能够被继承的,问题自然就解决了。
public final class SensitiveOperationFinal {public SensitiveOperationFinal(){if(!doSecurityCheck()){throw new SecurityException("Security check failed!");}}//Security check return falseprivate boolean doSecurityCheck(){return false;}public void storeMoney(){System.out.println("Store 1000000 RMB!");}
}
使用final finalize方法
因为子类想要重写finalize方法,如果我们的父类中finalize方法定义为final,也可以解决这个问题。
public final class SensitiveOperationFinal {public SensitiveOperationFinal(){if(!doSecurityCheck()){throw new SecurityException("Security check failed!");}}//Security check return falseprivate boolean doSecurityCheck(){return false;}public void storeMoney(){System.out.println("Store 1000000 RMB!");}final protected void finalize() {}
}
使用flag变量
我们可以在对象构建完毕的时候设置一个flag变量,然后在每次安全操作的时候都去判断一下这个flag变量,这样也可以避免之前提到的问题:
public class SensitiveOperationFlag {private volatile boolean flag= false;public SensitiveOperationFlag(){if(!doSecurityCheck()){throw new SecurityException("Security check failed!");}flag=true;}//Security check return falseprivate boolean doSecurityCheck(){return false;}public void storeMoney(){if(!flag){System.out.println("Object is not initiated yet!");return;}System.out.println("Store 1000000 RMB!");}
}
注意,这里flag需要设置为volatile,只有这样才能保证构造函数在flag设置之前执行。也就是说需要保证happens-before特性。
使用this或者super
在JDK6或者更高版本中,如果对象的构造函数在java.lang.Object构造函数退出之前引发异常,则JVM将不会执行该对象的finalize方法。
因为Java确保java.lang.Object构造函数在任何构造函数的第一条语句之上或之前执行。如果构造函数中的第一个语句是对超类的构造函数或同一个类中的另一个构造函数的调用,则java.lang.Object构造函数将在该调用中的某个位置执行。否则,Java将在该构造函数的代码中的任何一个执行之前执行超类的默认构造函数,并且将通过隐式调用执行java.lang.Object构造函数。
也就是说如果异常发生在构造函数中的第一条this或者super中的时候,JVM将不会调用对象的finalize方法:
public class SensitiveOperationThis {public SensitiveOperationThis(){this(doSecurityCheck());}private SensitiveOperationThis(boolean secure) {}//Security check return falseprivate static boolean doSecurityCheck(){throw new SecurityException("Security check failed!");}public void storeMoney(){System.out.println("Store 1000000 RMB!");}
}
本文的例子:
learn-java-base-9-to-20/tree/master/security
本文已收录于 http://www.flydean.com/java-security-code-line-object/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
java安全编码指南之:对象构建相关推荐
- java安全编码指南之:字符串和编码
文章目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中 ...
- java安全编码指南之:拒绝Denial of Service
文章目录 简介 为什么会有DOS 不合理的资源使用 请求用于矢量图的SVG文件和字体文件 字符串或二进制表示的图片转换 zip炸弹 billion laughs attack hashMap中插入太多 ...
- java安全编码指南之:序列化Serialization
文章目录 简介 序列化简介 注意serialVersionUID writeObject和readObject readResolve和writeReplace 不要序列化内部类 如果类中有自定义变量 ...
- java安全编码指南之:线程安全规则
文章目录 简介 注意线程安全方法的重写 构造函数中this的溢出 不要在类初始化的时候使用后台线程 简介 如果我们在多线程中引入了共享变量,那么我们就需要考虑一下多线程下线程安全的问题了.那么我们在编 ...
- java安全编码指南之:Thread API调用规则
文章目录 简介 start一个Thread 不要使用ThreadGroup 不要使用stop()方法 wait 和 await 需要放在循环中调用 简介 java中多线程的开发中少不了使用Thread ...
- java安全编码指南之:lock和同步的正确使用
文章目录 简介 使用private final object来作为lock对象 不要synchronize可被重用的对象 不要sync Object.getClass() 不要sync高级并发对象 不 ...
- java安全编码指南之:可见性和原子性
文章目录 简介 不可变对象的可见性 保证共享变量的复合操作的原子性 保证多个Atomic原子类操作的原子性 保证方法调用链的原子性 读写64bits的值 简介 java类中会定义很多变量,有类变量也有 ...
- java安全编码指南之:输入校验
文章目录 简介 在字符串标准化之后进行校验 注意不可信字符串的格式化 小心使用Runtime.exec() 正则表达式的匹配 简介 为了保证java程序的安全,任何外部用户的输入我们都认为是可能有恶意 ...
- java安全编码指南之:表达式规则
文章目录 简介 注意表达式的返回值 注意避免NullPointerException 数组相等的判断 基础类型的封装类间的比较 集合中类型不匹配 Asset的副作用 简介 在java编写过程中,我们会 ...
最新文章
- 在一个html加载多个echarts,Echarts一个页面加载多个图表及图表自适应
- 从java到c_Binder机制,从Java到C (4. Parcel)
- Python之PIL库的运用、GIF处理
- MVC3.0删除数据的时候给提示信息
- SpringMVC框架中ModelAndView、Model、ModelMap的区别与使用
- Ubuntu14.04 下截图工具与设置快捷键
- iOS JSPatch 热修复使用
- 北京市强化电信用户信息安全保护
- C# 获取打开的EXCEL中某列的行数
- c#读取csv到数组_C#读取CSV文件的方法
- pvs、pvdisplay、pvscan 查看物理卷信息
- 用手画了11张图终于搞明白了Git工作流,我怀疑你用的是假 Git
- 传奇LEG引擎武器衣服发光特效计算方式【适用于新blue/gob/goh/886m2等引擎通用】
- 测试电池损耗的软件运行原理,鲁大师电池损耗检测准确?鲁大师电池损耗检测原理解析...
- 求模板啊,求软著说明书模板啊
- OpenSSL解析X509证书
- 使用Excel批量生成sql,包括日期格式
- Fiddler中inspector的用法2-2
- (对对碰)软工结对作业
- 88个建筑施工问题合集,堪称教科书