java安全編程(轉(zhuǎn))
發(fā)表時(shí)間:2024-02-21 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干關(guān)鍵要點(diǎn): 不要使用公共域或變量;把它們聲明為私有的并提供訪問函數(shù)以限制對(duì)它們的訪問。除非有很好的理由,把方法都設(shè)為私有的(如果確實(shí)沒這樣做,說清楚其理由)。這些非私有的方法必須保護(hù)自己,因?yàn)樗鼈兛赡軙?huì)接收到受污染的數(shù)...
下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干關(guān)鍵要點(diǎn):
不要使用公共域或變量;把它們聲明為私有的并提供訪問函數(shù)以限制對(duì)它們的訪問。
除非有很好的理由,
把方法都設(shè)為私有的(如果確實(shí)沒這樣做,說清楚其理由)。這些非私有的方法必須保護(hù)自己,因?yàn)樗鼈兛赡軙?huì)接收到受污染的數(shù)據(jù)(除非已經(jīng)用其它方式對(duì)它們進(jìn)行了保護(hù))。
避免使用靜態(tài)域變量。這樣的變量附著在類(而非類的實(shí)例)上,而類可以被其它類所定位。其結(jié)果就是靜態(tài)域變量可以被其它類找到,因此很難保證它們的安全。
永遠(yuǎn)不要把可變對(duì)象返回給潛在有惡意的代碼(因?yàn)榇a可能會(huì)改變它)。注意,數(shù)組是可變的(即使數(shù)組的內(nèi)容不可變),所以不要返回一個(gè)含有敏感數(shù)據(jù)的內(nèi)部數(shù)組的引用。
永遠(yuǎn)不要直接保存用戶給定的可變對(duì)象(包括對(duì)象的數(shù)組)。否則,用戶可以把對(duì)象交給安全代碼,讓安全代碼“檢查”對(duì)象,并在安全代碼試圖使用數(shù)據(jù)時(shí)改變數(shù)據(jù)。應(yīng)該在內(nèi)部存儲(chǔ)數(shù)組前復(fù)制它們,而且要小心(例如,警惕用戶編寫的復(fù)制例程)。
不要依賴于初始化。有好幾種方法給未初始化的對(duì)象分配內(nèi)存。
除非有很好的理由,
應(yīng)該使每件事都是確定的。如果某個(gè)類或方法不是確定的,攻擊者就可以用某種危險(xiǎn)而無法預(yù)知的方法來擴(kuò)展它。注意,作為安全性的交換,這會(huì)帶來可擴(kuò)展性的喪失。
不要在安全性上依賴包的范圍。若干類,如java.lang,缺省是關(guān)閉的,而且某些Java虛擬機(jī)(JVM)會(huì)讓你關(guān)閉其它包。否則,Java類是沒有關(guān)閉的。因此,攻擊者可以向包中引入一個(gè)新類,并用此新類來訪問你以為保護(hù)了的信息。
不要使用內(nèi)部類。在內(nèi)部類轉(zhuǎn)換為字節(jié)代碼時(shí),內(nèi)部類會(huì)轉(zhuǎn)換為可以訪問包中任意類的類。更糟的是,被封裝類的私有域靜悄悄地變成非私有的,允許內(nèi)部類訪問!
最小化特權(quán)。如果可能,完全不要請(qǐng)求任何特殊的許可。McGraw更進(jìn)一步地推薦不要標(biāo)記任何代碼;我認(rèn)為可以標(biāo)記代碼(這樣用戶可以決定“只有列表上的發(fā)送者可以運(yùn)行標(biāo)記過的代碼”),但在編寫程序時(shí)要使程序不需要沙箱設(shè)置之外的權(quán)限。如果一定要有更大的權(quán)限,審讀代碼就會(huì)特別困難。
如果一定要標(biāo)記代碼,應(yīng)該把它們都放在一個(gè)檔案文件里。這里最好引用McGraw [1999]的原文:
此規(guī)則的目的是防止攻擊者使用混合匹配攻擊,構(gòu)建新applet或庫把某些標(biāo)記類與有惡意的類連接在一起,或者把根本意識(shí)不到會(huì)被一起使用的標(biāo)記類連接在一起。通過把一組類標(biāo)記在一起,就可以使這種攻擊更困難,F(xiàn)有的代碼標(biāo)記系統(tǒng)在防止混合匹配攻擊上做得還不夠,所以這一規(guī)則還不能完全防止此類攻擊。但使用單個(gè)答案沒什么壞處。應(yīng)該使類不可被復(fù)制。Java的類復(fù)制機(jī)制允許攻擊者不運(yùn)行構(gòu)建函數(shù)就實(shí)例化某個(gè)類。要使類不可被復(fù)制,只要在每個(gè)類里定義如下方法:
public final void clone() throws java.lang.CloneNotSupportedException {
throw new java.lang.CloneNotSupportedException();
}
如果確實(shí)需要使類可被復(fù)制,那么可以采用幾個(gè)保護(hù)措施來防止攻擊者重新定義復(fù)制方法。如果是定義自己的復(fù)制方法,只需要使它是確定的。如果不是定義自己的復(fù)制方法,至少可以通過增加如下內(nèi)容來防止復(fù)制方法被惡意地重載:
public final void clone() throws java.lang.CloneNotSupportedException {
super.clone();
}
應(yīng)該使類不可序列化。系列化運(yùn)行攻擊者看到對(duì)象的內(nèi)部狀態(tài),甚至私有部分。要防止這一點(diǎn),需要在類里增加如下方法:
private final void writeObject(ObjectOutputStream out)
throws java.io.IOException {
throw new java.io.IOException("Object cannot be serialized");
}
甚至在序列化沒問題的情況下,也應(yīng)該對(duì)包含直接處理系統(tǒng)資源的域和包含與地址空間有關(guān)信息的域使用臨時(shí)關(guān)鍵字。否則,解除類的序列化就會(huì)允許不適當(dāng)?shù)脑L問?赡苓需要把敏感信息標(biāo)識(shí)為臨時(shí)的。
如果對(duì)類定義了自己的序列化方法,就
不應(yīng)該把內(nèi)部數(shù)組傳遞給需要數(shù)組的DataInput/DataOuput方法。其理由在于:所有的DataInput/DataOuput方法都可以被重載。如果某個(gè)可序列化的類向某個(gè)DataOutput(write(byte [] b))方法直接傳遞了一個(gè)私有數(shù)組,那么攻擊者就可以構(gòu)建子類ObjectOutputStream并重載write(byte [] b)方法,從而可以訪問并修改那個(gè)私有數(shù)組。注意,缺省的序列化并沒有把私有字節(jié)數(shù)組域暴露給DataInput/DataOutput字節(jié)數(shù)組方法。
應(yīng)該使類不可被解除序列化。即使類不可被序列化,它依然可以被解除序列化。攻擊者可以構(gòu)建一個(gè)字節(jié)序列,使它碰巧是被解除序列化的某個(gè)類實(shí)例,而且具有攻擊者選定的值。換句化話說,解除序列化是一種公共的構(gòu)建函數(shù),允許攻擊者選擇對(duì)象的狀態(tài) -- 顯然是一個(gè)危險(xiǎn)的操作! 要防止這一點(diǎn),需要在類里增加如下方法:
private final void readObject(ObjectInputStream in)
throws java.io.IOException {
throw new java.io.IOException("Class cannot be deserialized");
}
不要通過名稱來比較類。畢竟攻擊者可以用相同的名稱定義類,而且一不小心就會(huì)授予這些類不恰當(dāng)?shù)臋?quán)限。因此,下面是一個(gè)判斷某個(gè)對(duì)象是否含有某個(gè)給定類的錯(cuò)誤方法的例子:
if (obj.getClass().getName().equals("Foo")) {
如果要判斷兩個(gè)對(duì)象是否含有完全相同的類,
不要對(duì)雙方使用getClass()并使用“==”操作符進(jìn)行比較,而應(yīng)該使用如下形式:
if (a.getClass() == b.getClass()) {
如果確實(shí)需要判斷某個(gè)對(duì)象是否含有某個(gè)給定類名,
需要嚴(yán)格按照規(guī)范并確保使用當(dāng)前名稱空間(當(dāng)前類的ClassLoader所在名稱空間)。因此,應(yīng)該使用如下形式:
if (obj.getClass() == this.getClassLoader().loadClass("Foo")) {
本原則來自McGraw和Felten,而且確實(shí)是個(gè)好原則。要補(bǔ)充的是,盡可能地避免比較類值通常是個(gè)好注意。通常最好是盡力設(shè)計(jì)類的方法和接口,從而完全不必要做這些事。盡管如此,實(shí)際上無法完全做到,所以知道這些技巧還是很重要的。
不要把秘密(密鑰、密碼或算法)存儲(chǔ)在代碼或數(shù)據(jù)里。有惡意的JVM可以迅速看到這一數(shù)據(jù)。打亂代碼并不能在認(rèn)真的攻擊者面前實(shí)際隱藏代碼。