一初婆、開(kāi)發(fā)安全風(fēng)險(xiǎn)評(píng)估
序 | 開(kāi)發(fā)安全規(guī)范 | 嚴(yán)重性 | 整改代價(jià) | 優(yōu)先級(jí) | 級(jí)別 |
---|---|---|---|---|---|
1 | 在返回引用之前屁置,防御性復(fù)制私有的可變的類成員 | 高 | 中 | 12 | 1 |
2 | 小心處理構(gòu)造函數(shù)拋出異常的情況 | 高 | 中 | 12 | 1 |
3 | 聲明數(shù)據(jù)成員為私有并提供可訪問(wèn)的封裝器方法 | 中 | 中 | 12 | 1 |
4 | 比較類而不是進(jìn)行類名稱 | 高 | 低 | 9 | 2 |
5 | 不允許敏感類復(fù)制其自身 | 中 | 中 | 8 | 2 |
6 | 不要在嵌套類中暴露外部類的私有字段 | 中 | 中 | 8 | 2 |
7 | 不要使用公有靜態(tài)的非final變量 | 中 | 中 | 8 | 2 |
- 級(jí)別:L1高危害划址、可利用性強(qiáng)该默、整改代價(jià)低般贼;
- L2中危害瘸恼、可能被利用、整改代價(jià)中似谁;
- 優(yōu)先級(jí):數(shù)值越高傲绣,越優(yōu)先修復(fù);
- 來(lái)源:Java安全編碼標(biāo)準(zhǔn)/朗(Long巩踏, F.)等著秃诵,第六章
二、開(kāi)發(fā)安全規(guī)范說(shuō)明
1塞琼、在返回引用之前菠净,防御性復(fù)制私有的可變的類成員
返回類的內(nèi)部可變成員的引用,會(huì)破壞一個(gè)應(yīng)用的安全性,這個(gè)破壞既體現(xiàn)在對(duì)封裝性的破壞嗤练,也體現(xiàn)在為破壞類的內(nèi)部狀態(tài)提供了可能性。因此程序禁止返回內(nèi)部可變類成員的引用在讶。
返回一個(gè)指向可變內(nèi)部狀態(tài)的防御性復(fù)制的引用煞抬,能保證調(diào)用者不會(huì)修改初始的內(nèi)部狀態(tài),盡管這個(gè)副本仍然是可變的构哺。
- 【情況1】返回私有對(duì)象引用或包含不可變對(duì)象的可變成員革答,使用
clone()
返回對(duì)象的克隆副本。
Public class Test_1_1 {
Private Date date; // 對(duì)象引用
Private HashMap<Integer曙强,String> hm = new HashMap<Integer残拐,String>(); // 包含不可變對(duì)象的可變成員
Public Date getDate(){
return (Date)date.clone();
}
Public String getString(int i){
Return hm.get(i); // 直接return hm是不安全的。
}
Public HashMap<Integer碟嘴,String> getMap() {
return (HashMap<Integer溪食,String>) hm.clone();
}
}
注意:若類會(huì)被非受信代碼擴(kuò)展時(shí),在執(zhí)行一個(gè)構(gòu)造方法的防御性復(fù)制時(shí)娜扇,必須避免使用clone()
方法错沃,改用原始的“創(chuàng)建對(duì)象,逐一賦值”的方法雀瓢,防止執(zhí)行被惡意覆寫了的clone()
方法枢析。
- 【情況2】返回私有可變對(duì)象數(shù)組,因?yàn)閿?shù)組中包含的對(duì)象引用是可變的刃麸,對(duì)數(shù)組進(jìn)行淺復(fù)制是不夠的醒叁,需采用深度復(fù)制。
Public class Test_1_2 {
Private Date[] dates;
Public Date[] getDates(){
Date[] newDates = new Date[dates.length];
For(int i = 0; i < dates.length; i++){
newDates[i] = dates[i].clone();
}
Return newDates;
}
}
2泊业、小心處理構(gòu)造函數(shù)拋出異常的情況
在一個(gè)構(gòu)造方法開(kāi)始創(chuàng)建對(duì)象把沼,但并未結(jié)束時(shí),對(duì)象只會(huì)被部分地初始化吁伺。
其他類可能會(huì)從并發(fā)運(yùn)行的線程中訪問(wèn)一個(gè)部分初始化的對(duì)象智政,在沒(méi)有原子性失效的時(shí)候,部分初始化會(huì)導(dǎo)致對(duì)象處于不一致的狀態(tài)箱蝠。
處理部分初始化對(duì)象的問(wèn)題有3種通用的解決方法:
在對(duì)象的構(gòu)造方法中拋出異常续捂。遺憾的是,攻擊者會(huì)惡意地獲得這樣一個(gè)對(duì)象的實(shí)例宦搬。例如牙瓢,一個(gè)使用終止器構(gòu)造方法的攻擊允許攻擊者調(diào)用類中的任意方法,即使這個(gè)類方法是由安全管理器保護(hù)的间校。
聲明對(duì)象的被初始化變量為final可以防止對(duì)象被部分初始化矾克。當(dāng)一個(gè)線程中執(zhí)行的構(gòu)造方法將一個(gè)final數(shù)據(jù)成員初始化成一個(gè)已知的安全值時(shí),另外的線程不能看到這個(gè)對(duì)象任何初始化之前的值憔足。
初始化標(biāo)志胁附。這個(gè)方法允許未初始化或者部分初始化的對(duì)象在已知的失效狀態(tài)下存在酒繁,這樣的對(duì)象就是我們通常認(rèn)為的僵尸對(duì)象(zombie object)。這個(gè)方案容易出錯(cuò)控妻,因?yàn)槿魏螌?duì)這樣一個(gè)類的訪問(wèn)必須要先檢查對(duì)象是否被正確地初始化州袒。
方案 | 未初始化值 | 部分初始化值 |
---|---|---|
構(gòu)造函數(shù)中的異常 | 阻止 | 不阻止 |
Final數(shù)據(jù)成員(推薦) | 阻止 | 阻止 |
初始化標(biāo)志 | 檢測(cè) | 檢測(cè) |
3、聲明數(shù)據(jù)成員為私有并提供可訪問(wèn)的封裝器方法
控制聲明為public
或者protected
的數(shù)據(jù)成員的訪問(wèn)方式是很困難的弓候,攻擊者會(huì)用意想不到的方式控制這些成員郎哭。
因此,數(shù)據(jù)成員必須聲明為private
菇存,并使用public
或者protected
的封裝器方法提供數(shù)據(jù)訪問(wèn)夸研,監(jiān)視并控制對(duì)數(shù)據(jù)成員的修改,保持類的不變性依鸥。
當(dāng)數(shù)據(jù)成員是私有可變對(duì)象的引用時(shí)亥至,要特別注意不要直接返回引用,詳見(jiàn)規(guī)則1贱迟。
【例外】當(dāng)類僅作為數(shù)據(jù)結(jié)構(gòu)抬闯,而沒(méi)有任何行為時(shí),可以聲明數(shù)據(jù)成員為public关筒。
4溶握、比較類而不是進(jìn)行類名稱
在JVM中, “如果它們被同一個(gè)類裝載器裝載蒸播,并且有相同的全名睡榆,那么這兩個(gè)類被認(rèn)為是相同的類(并且因此有相同的類型)”。
但是袍榆,有同樣名稱卻來(lái)自不同包名的兩個(gè)類是不同的類胀屿,因此不能基于類名稱進(jìn)行比較。
- 不符合規(guī)則的案例1:
// 基于類名稱進(jìn)行比較是不準(zhǔn)確的
a.getClass().getName().equals(b.getClass().getName())
- 不符合規(guī)則的案例2:
// 比較類的全限定名也是不夠的包雀,
// 因?yàn)椴煌念愌b載器會(huì)裝載具有相同全限定名的不同的類到一個(gè)JVM中宿崭。
a.getClass().getName().equals(“com.test.a”)
- 符合規(guī)則的案例1:
a.getClass() == this.getClass().getClassLoader().loadClass(“com.test.a”);
- 符合規(guī)則的案例2:
a.getClass() == b.getClass()
5、不允許敏感類復(fù)制其自身
最好不要復(fù)制包含私有才写、保密或者其他敏感數(shù)據(jù)的類葡兑。
如果一個(gè)類是不準(zhǔn)備被復(fù)制的,但它又沒(méi)有定義復(fù)制機(jī)制赞草,只通過(guò)構(gòu)造函數(shù)進(jìn)行安全檢查讹堤,是不足以防止復(fù)制的。
Java的對(duì)象克隆機(jī)制clone()允許攻擊者通過(guò)復(fù)制已有對(duì)象的內(nèi)存鏡像來(lái)創(chuàng)建一個(gè)類的新實(shí)例厨疙,而不是通過(guò)執(zhí)行這個(gè)類的構(gòu)造方法來(lái)創(chuàng)建新實(shí)例洲守。
利用該機(jī)制,通過(guò)創(chuàng)建子類(構(gòu)造方法中繞過(guò)父類的安全檢測(cè))和克隆子類實(shí)現(xiàn)敏感類的復(fù)制。
符合規(guī)則的方案1:
最容易防止利用惡意子類克隆的方法是聲明類為final梗醇。符合規(guī)則的方案2:
定義一個(gè)總是拋出CloneNotSupportedException
錯(cuò)誤的final clone
方法知允,防止子類成為可克隆的。
6叙谨、不要在嵌套類中暴露外部類的私有字段
嵌套類指那些聲明在另一個(gè)類或者接口代碼塊中的類温鸽,嵌套類可以訪問(wèn)外被類的私有變量。
當(dāng)嵌套類被聲明為public或者當(dāng)它包含public的方法或者構(gòu)造方法時(shí)唉俗,可以被任何在同一個(gè)包里的其他類訪問(wèn)嗤朴。
因此配椭,嵌套類禁止將外部類的私有成員暴露給外部的類或者包虫溜。
- 不符合規(guī)則的案例:
class Test_7_1 {
Private int secret;
Public class InnerClass { // 嵌套類
Public int get(){ return secret; }
}
}
Class Test_7_2 {
Public static void main(String[] args) {
Test_7_1 t = new Test_7_1();
Test_7_1.InnerClass c = t. new InnerClass();
c.get(); // 這里獲得Test_7_1的私有變量secret
}
}
- 符合規(guī)則的案例:
將InnerClass
聲明為private
來(lái)隱藏嵌套類,或?qū)⒖赡鼙┞端接谐蓡T的方法聲明為private
股缸。
7衡楞、不要使用公有靜態(tài)的非final變量
客戶代碼可以輕易地訪問(wèn)到公有靜態(tài)數(shù)據(jù)成員。
安全管理器不會(huì)對(duì)讀取或?qū)懭脒@些變量進(jìn)行檢查敦姻。此外瘾境,在將新值存儲(chǔ)到這些字段之前,是不能通過(guò)編程方式進(jìn)行驗(yàn)證的镰惦。
在多線程的場(chǎng)合中迷守,非final的公有靜態(tài)字段會(huì)被不一致的方式修改。因此旺入,類必須不能包含非final的公有靜態(tài)字段兑凿。
符合規(guī)則的案例:
public static final FuncLoader m_functions;