java安全编码指南之:对象构建操作
简介
程序员肯定是不缺对象的,因为随时都可以构建一个,对象多了肯定会出现点安全问题,一起来看看在java的对象构建中怎么保证对象的安全性吧。
构造函数的异常
考虑下面的一个例子:
publicclassSensitiveOperation{ publicSensitiveOperation(){ if(!doSecurityCheck()){ thrownewSecurityException("Securitycheckfailed!"); } } //Securitycheckreturnfalse privatebooleandoSecurityCheck(){ returnfalse; } publicvoidstoreMoney(){ System.out.println("Store1000000RMB!"); } }
上面的例子中,我们在构造函数中做了一个securityCheck,因为这个securityCheck返回的值是false,所以会抛出SecurityException。
看下调用的例子:
publicstaticvoidmain(String[]args){ SensitiveOperationsensitiveOperation=newSensitiveOperation(); sensitiveOperation.storeMoney(); }
这个调用会抛出下面的异常:
Exceptioninthread"main"java.lang.SecurityException:Securitycheckfailed! atcom.flydean.SensitiveOperation.(SensitiveOperation.java:11) atcom.flydean.SensitiveUsage.main(SensitiveUsage.java:10)
那么问题来了,上面的这个class是不是安全的呢?
FinalizerAttack
上面的class不是final的,所以我们可以构造一个class去继承它。然后考虑这样一个问题,当构造函数抛出异常之后,会执行什么操作呢?
如果该对象已经被构建了,那么这个对象在GC的时候需要执行finalize方法。那么我们是不是可以在finalize方法中绕过安全检查呢?
看下面的例子:
publicclassSensitiveOperationFinalizerextendsSensitiveOperation{ publicSensitiveOperationFinalizer(){ } @Override protectedvoidfinalize(){ System.out.println("WecanstilldostoreMoneyaction!"); this.storeMoney(); System.exit(0); } }
上的例子中,我们继承了SensitiveOperation,并且实现了finalize方法,在finalize中,我们调用了storeMoney。看下运行的代码:
publicvoidtestFinalizer()throwsInterruptedException{ try{ SensitiveOperationsensitiveOperation=newSensitiveOperationFinalizer(); sensitiveOperation.storeMoney(); }catch(Exceptione){ System.out.println(e.getMessage()); } System.gc(); Thread.sleep(10000); }
运行结果:
Securitycheckfailed! WecanstilldostoreMoneyaction! Store1000000RMB!
可以看到,虽然我们构造函数抛出了异常,但是storeMoney的操作还是被执行了!
这个操作就叫做FinalizerAttack。
解决FinalizerAttack
怎么解决这个构造函数抛出异常的问题呢?这里给大家介绍几种解决方法。
使用finalclass
如果使用finalclass,那么类是不能够被继承的,问题自然就解决了。
publicfinalclassSensitiveOperationFinal{ publicSensitiveOperationFinal(){ if(!doSecurityCheck()){ thrownewSecurityException("Securitycheckfailed!"); } } //Securitycheckreturnfalse privatebooleandoSecurityCheck(){ returnfalse; } publicvoidstoreMoney(){ System.out.println("Store1000000RMB!"); } }
使用finalfinalize方法
因为子类想要重写finalize方法,如果我们的父类中finalize方法定义为final,也可以解决这个问题。
publicfinalclassSensitiveOperationFinal{ publicSensitiveOperationFinal(){ if(!doSecurityCheck()){ thrownewSecurityException("Securitycheckfailed!"); } } //Securitycheckreturnfalse privatebooleandoSecurityCheck(){ returnfalse; } publicvoidstoreMoney(){ System.out.println("Store1000000RMB!"); } finalprotectedvoidfinalize(){ } }
使用flag变量
我们可以在对象构建完毕的时候设置一个flag变量,然后在每次安全操作的时候都去判断一下这个flag变量,这样也可以避免之前提到的问题:
publicclassSensitiveOperationFlag{ privatevolatilebooleanflag=false; publicSensitiveOperationFlag(){ if(!doSecurityCheck()){ thrownewSecurityException("Securitycheckfailed!"); } flag=true; } //Securitycheckreturnfalse privatebooleandoSecurityCheck(){ returnfalse; } publicvoidstoreMoney(){ if(!flag){ System.out.println("Objectisnotinitiatedyet!"); return; } System.out.println("Store1000000RMB!"); } }
注意,这里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方法:
publicclassSensitiveOperationThis{ publicSensitiveOperationThis(){ this(doSecurityCheck()); } privateSensitiveOperationThis(booleansecure){ } //Securitycheckreturnfalse privatestaticbooleandoSecurityCheck(){ thrownewSecurityException("Securitycheckfailed!"); } publicvoidstoreMoney(){ System.out.println("Store1000000RMB!"); } }
本文的例子:
learn-java-base-9-to-20/tree/master/security
以上这篇java安全编码指南之:对象构建操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。