明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

java安全編程(轉(zhuǎn))

[摘要]下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干關(guān)鍵要點: 不要使用公共域或變量;把它們聲明為私有的并提供訪問函數(shù)以限制對它們的訪問。除非有很好的理由,把方法都設(shè)為私有的(如果確實沒這樣做,說清楚其理由)。這些非私有的方法必須保護自己,因為它們可能會接收到受污染的數(shù)...
下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干關(guān)鍵要點:


不要使用公共域或變量;把它們聲明為私有的并提供訪問函數(shù)以限制對它們的訪問。

除非有很好的理由,把方法都設(shè)為私有的(如果確實沒這樣做,說清楚其理由)。這些非私有的方法必須保護自己,因為它們可能會接收到受污染的數(shù)據(jù)(除非已經(jīng)用其它方式對它們進行了保護)。

避免使用靜態(tài)域變量。這樣的變量附著在類(而非類的實例)上,而類可以被其它類所定位。其結(jié)果就是靜態(tài)域變量可以被其它類找到,因此很難保證它們的安全。

永遠不要把可變對象返回給潛在有惡意的代碼(因為代碼可能會改變它)。注意,數(shù)組是可變的(即使數(shù)組的內(nèi)容不可變),所以不要返回一個含有敏感數(shù)據(jù)的內(nèi)部數(shù)組的引用。

永遠不要直接保存用戶給定的可變對象(包括對象的數(shù)組)。否則,用戶可以把對象交給安全代碼,讓安全代碼“檢查”對象,并在安全代碼試圖使用數(shù)據(jù)時改變數(shù)據(jù)。應(yīng)該在內(nèi)部存儲數(shù)組前復制它們,而且要小心(例如,警惕用戶編寫的復制例程)。

不要依賴于初始化。有好幾種方法給未初始化的對象分配內(nèi)存。

除非有很好的理由,應(yīng)該使每件事都是確定的。如果某個類或方法不是確定的,攻擊者就可以用某種危險而無法預知的方法來擴展它。注意,作為安全性的交換,這會帶來可擴展性的喪失。

不要在安全性上依賴包的范圍。若干類,如java.lang,缺省是關(guān)閉的,而且某些Java虛擬機(JVM)會讓你關(guān)閉其它包。否則,Java類是沒有關(guān)閉的。因此,攻擊者可以向包中引入一個新類,并用此新類來訪問你以為保護了的信息。

不要使用內(nèi)部類。在內(nèi)部類轉(zhuǎn)換為字節(jié)代碼時,內(nèi)部類會轉(zhuǎn)換為可以訪問包中任意類的類。更糟的是,被封裝類的私有域靜悄悄地變成非私有的,允許內(nèi)部類訪問!

最小化特權(quán)。如果可能,完全不要請求任何特殊的許可。McGraw更進一步地推薦不要標記任何代碼;我認為可以標記代碼(這樣用戶可以決定“只有列表上的發(fā)送者可以運行標記過的代碼”),但在編寫程序時要使程序不需要沙箱設(shè)置之外的權(quán)限。如果一定要有更大的權(quán)限,審讀代碼就會特別困難。

如果一定要標記代碼,應(yīng)該把它們都放在一個檔案文件里。這里最好引用McGraw [1999]的原文:

此規(guī)則的目的是防止攻擊者使用混合匹配攻擊,構(gòu)建新applet或庫把某些標記類與有惡意的類連接在一起,或者把根本意識不到會被一起使用的標記類連接在一起。通過把一組類標記在一起,就可以使這種攻擊更困難,F(xiàn)有的代碼標記系統(tǒng)在防止混合匹配攻擊上做得還不夠,所以這一規(guī)則還不能完全防止此類攻擊。但使用單個答案沒什么壞處。



應(yīng)該使類不可被復制。Java的類復制機制允許攻擊者不運行構(gòu)建函數(shù)就實例化某個類。要使類不可被復制,只要在每個類里定義如下方法:


public final void clone() throws java.lang.CloneNotSupportedException {
 throw new java.lang.CloneNotSupportedException();
 }



如果確實需要使類可被復制,那么可以采用幾個保護措施來防止攻擊者重新定義復制方法。如果是定義自己的復制方法,只需要使它是確定的。如果不是定義自己的復制方法,至少可以通過增加如下內(nèi)容來防止復制方法被惡意地重載:


public final void clone() throws java.lang.CloneNotSupportedException {
super.clone();
}




應(yīng)該使類不可序列化。系列化運行攻擊者看到對象的內(nèi)部狀態(tài),甚至私有部分。要防止這一點,需要在類里增加如下方法:


private final void writeObject(ObjectOutputStream out)
throws java.io.IOException {
 throw new java.io.IOException("Object cannot be serialized");
}




甚至在序列化沒問題的情況下,也應(yīng)該對包含直接處理系統(tǒng)資源的域和包含與地址空間有關(guān)信息的域使用臨時關(guān)鍵字。否則,解除類的序列化就會允許不適當?shù)脑L問。可能還需要把敏感信息標識為臨時的。

如果對類定義了自己的序列化方法,就不應(yīng)該把內(nèi)部數(shù)組傳遞給需要數(shù)組的DataInput/DataOuput方法。其理由在于:所有的DataInput/DataOuput方法都可以被重載。如果某個可序列化的類向某個DataOutput(write(byte [] b))方法直接傳遞了一個私有數(shù)組,那么攻擊者就可以構(gòu)建子類ObjectOutputStream并重載write(byte [] b)方法,從而可以訪問并修改那個私有數(shù)組。注意,缺省的序列化并沒有把私有字節(jié)數(shù)組域暴露給DataInput/DataOutput字節(jié)數(shù)組方法。

應(yīng)該使類不可被解除序列化。即使類不可被序列化,它依然可以被解除序列化。攻擊者可以構(gòu)建一個字節(jié)序列,使它碰巧是被解除序列化的某個類實例,而且具有攻擊者選定的值。換句化話說,解除序列化是一種公共的構(gòu)建函數(shù),允許攻擊者選擇對象的狀態(tài) -- 顯然是一個危險的操作! 要防止這一點,需要在類里增加如下方法:


private final void readObject(ObjectInputStream in)
throws java.io.IOException {
throw new java.io.IOException("Class cannot be deserialized");
}




不要通過名稱來比較類。畢竟攻擊者可以用相同的名稱定義類,而且一不小心就會授予這些類不恰當?shù)臋?quán)限。因此,下面是一個判斷某個對象是否含有某個給定類的錯誤方法的例子:


 if (obj.getClass().getName().equals("Foo")) {




如果要判斷兩個對象是否含有完全相同的類,不要對雙方使用getClass()并使用“==”操作符進行比較,而應(yīng)該使用如下形式: 


if (a.getClass() == b.getClass()) {



如果確實需要判斷某個對象是否含有某個給定類名,需要嚴格按照規(guī)范并確保使用當前名稱空間(當前類的ClassLoader所在名稱空間)。因此,應(yīng)該使用如下形式:


 if (obj.getClass() == this.getClassLoader().loadClass("Foo")) {




本原則來自McGraw和Felten,而且確實是個好原則。要補充的是,盡可能地避免比較類值通常是個好注意。通常最好是盡力設(shè)計類的方法和接口,從而完全不必要做這些事。盡管如此,實際上無法完全做到,所以知道這些技巧還是很重要的。

不要把秘密(密鑰、密碼或算法)存儲在代碼或數(shù)據(jù)里。有惡意的JVM可以迅速看到這一數(shù)據(jù)。打亂代碼并不能在認真的攻擊者面前實際隱藏代碼。



相關(guān)文章