java安全編碼指南之:對(duì)象構(gòu)建操作
簡(jiǎn)介
程序員肯定是不缺對(duì)象的,因?yàn)殡S時(shí)都可以構(gòu)建一個(gè),對(duì)象多了肯定會(huì)出現(xiàn)點(diǎn)安全問(wèn)題,一起來(lái)看看在java的對(duì)象構(gòu)建中怎么保證對(duì)象的安全性吧。
構(gòu)造函數(shù)的異常
考慮下面的一個(gè)例子:
public class SensitiveOperation { public SensitiveOperation(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } } //Security check return false private boolean doSecurityCheck(){ return false; } public void storeMoney(){ System.out.println("Store 1000000 RMB!"); } }
上面的例子中,我們?cè)跇?gòu)造函數(shù)中做了一個(gè)securityCheck,因?yàn)檫@個(gè)securityCheck返回的值是false,所以會(huì)拋出SecurityException。
看下調(diào)用的例子:
public static void main(String[] args) { SensitiveOperation sensitiveOperation = new SensitiveOperation(); sensitiveOperation.storeMoney(); }
這個(gè)調(diào)用會(huì)拋出下面的異常:
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)
那么問(wèn)題來(lái)了,上面的這個(gè)class是不是安全的呢?
Finalizer Attack
上面的class不是final的,所以我們可以構(gòu)造一個(gè)class去繼承它。然后考慮這樣一個(gè)問(wèn)題,當(dāng)構(gòu)造函數(shù)拋出異常之后,會(huì)執(zhí)行什么操作呢?
如果該對(duì)象已經(jīng)被構(gòu)建了,那么這個(gè)對(duì)象在GC的時(shí)候需要執(zhí)行finalize方法。那么我們是不是可以在finalize方法中繞過(guò)安全檢查呢?
看下面的例子:
public class SensitiveOperationFinalizer extends SensitiveOperation{ public SensitiveOperationFinalizer(){ } @Override protected void finalize() { System.out.println("We can still do store Money action!"); this.storeMoney(); System.exit(0); } }
上的例子中,我們繼承了SensitiveOperation,并且實(shí)現(xiàn)了finalize方法,在finalize中,我們調(diào)用了storeMoney??聪逻\(yùn)行的代碼:
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); }
運(yùn)行結(jié)果:
Security check failed! We can still do store Money action! Store 1000000 RMB!
可以看到,雖然我們構(gòu)造函數(shù)拋出了異常,但是storeMoney的操作還是被執(zhí)行了!
這個(gè)操作就叫做Finalizer Attack。
解決Finalizer Attack
怎么解決這個(gè)構(gòu)造函數(shù)拋出異常的問(wèn)題呢?這里給大家介紹幾種解決方法。
使用final class
如果使用final class,那么類(lèi)是不能夠被繼承的,問(wèn)題自然就解決了。
public final class SensitiveOperationFinal { public SensitiveOperationFinal(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } } //Security check return false private boolean doSecurityCheck(){ return false; } public void storeMoney(){ System.out.println("Store 1000000 RMB!"); } }
使用final finalize方法
因?yàn)樽宇?lèi)想要重寫(xiě)finalize方法,如果我們的父類(lèi)中finalize方法定義為final,也可以解決這個(gè)問(wèn)題。
public final class SensitiveOperationFinal { public SensitiveOperationFinal(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } } //Security check return false private boolean doSecurityCheck(){ return false; } public void storeMoney(){ System.out.println("Store 1000000 RMB!"); } final protected void finalize() { } }
使用flag變量
我們可以在對(duì)象構(gòu)建完畢的時(shí)候設(shè)置一個(gè)flag變量,然后在每次安全操作的時(shí)候都去判斷一下這個(gè)flag變量,這樣也可以避免之前提到的問(wèn)題:
public class SensitiveOperationFlag { private volatile boolean flag= false; public SensitiveOperationFlag(){ if(!doSecurityCheck()){ throw new SecurityException("Security check failed!"); } flag=true; } //Security check return false private 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需要設(shè)置為volatile,只有這樣才能保證構(gòu)造函數(shù)在flag設(shè)置之前執(zhí)行。也就是說(shuō)需要保證happens-before特性。
使用this或者super
在JDK6或者更高版本中,如果對(duì)象的構(gòu)造函數(shù)在java.lang.Object構(gòu)造函數(shù)退出之前引發(fā)異常,則JVM將不會(huì)執(zhí)行該對(duì)象的finalize方法。
因?yàn)镴ava確保java.lang.Object構(gòu)造函數(shù)在任何構(gòu)造函數(shù)的第一條語(yǔ)句之上或之前執(zhí)行。如果構(gòu)造函數(shù)中的第一個(gè)語(yǔ)句是對(duì)超類(lèi)的構(gòu)造函數(shù)或同一個(gè)類(lèi)中的另一個(gè)構(gòu)造函數(shù)的調(diào)用,則java.lang.Object構(gòu)造函數(shù)將在該調(diào)用中的某個(gè)位置執(zhí)行。否則,Java將在該構(gòu)造函數(shù)的代碼中的任何一個(gè)執(zhí)行之前執(zhí)行超類(lèi)的默認(rèn)構(gòu)造函數(shù),并且將通過(guò)隱式調(diào)用執(zhí)行java.lang.Object構(gòu)造函數(shù)。
也就是說(shuō)如果異常發(fā)生在構(gòu)造函數(shù)中的第一條this或者super中的時(shí)候,JVM將不會(huì)調(diào)用對(duì)象的finalize方法:
public class SensitiveOperationThis { public SensitiveOperationThis(){ this(doSecurityCheck()); } private SensitiveOperationThis(boolean secure) { } //Security check return false private 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
以上這篇java安全編碼指南之:對(duì)象構(gòu)建操作就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot實(shí)現(xiàn)阿里云快遞物流查詢的示例代碼
本文將基于springboot實(shí)現(xiàn)快遞物流查詢,物流信息的獲取通過(guò)阿里云第三方實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2021-10-10Springboot允許logger.debug輸出日志方式
這篇文章主要介紹了Springboot允許logger.debug輸出日志方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Spring實(shí)戰(zhàn)之容器中的工程Bean用法示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之容器中的工程Bean用法,結(jié)合實(shí)例形式分析了Sring框架容器中的工程Bean相關(guān)配置、使用操作技巧,需要的朋友可以參考下2019-11-11深入了解Spring Boot2.3.0及以上版本的Liveness和Readiness功能
這篇文章主要介紹了Spring Boot2.3.0及以上版本的Liveness和Readiness功能示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10解決Maven本地倉(cāng)庫(kù)明明有對(duì)應(yīng)的jar包但還是報(bào)找不到的問(wèn)題
這篇文章主要介紹了解決Maven本地倉(cāng)庫(kù)明明有對(duì)應(yīng)的jar包但還是報(bào)找不到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Springboot項(xiàng)目引入druid安裝部署使用教程
這篇文章主要介紹了Springboot項(xiàng)目引入druid安裝部署使用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01解析Java編程之Synchronized鎖住的對(duì)象
這篇文章主要介紹了解析Java編程之Synchronized鎖住的對(duì)象,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10