下面是基于Gong [1999]、McGraw [1999]和Sun的指南的若干關鍵要點: public final void clone() throws java.lang.CloneNotSupportedException { throw new java.lang.CloneNotSupportedException(); } 如果確實需要使類可被復制,那么可以采用幾個保護措施來防止攻擊者重新定義復制方法。如果是定義自己的復制方法,只需要使它是確定的。如果不是定義自己的復制方法,至少可以通過增加如下內容來防止復制方法被惡意地重載: public final void clone() throws java.lang.CloneNotSupportedException { super.clone(); } 應該使類不可序列化。系列化運行攻擊者看到對象的內部狀態,甚至私有部分。要防止這一點,需要在類里增加如下方法: private final void writeObject(ObjectOutputStream out) throws java.io.IOException { throw new java.io.IOException("Object cannot be serialized"); } 甚至在序列化沒問題的情況下,也應該對包含直接處理系統資源的域和包含與地址空間有關信息的域使用臨時關鍵字。否則,解除類的序列化就會允許不適當的訪問。可能還需要把敏感信息標識為臨時的。 如果對類定義了自己的序列化方法,就不應該把內部數組傳遞給需要數組的DataInput/DataOuput方法。其理由在于:所有的DataInput/DataOuput方法都可以被重載。如果某個可序列化的類向某個DataOutput(write(byte [] b))方法直接傳遞了一個私有數組,那么攻擊者就可以構建子類ObjectOutputStream并重載write(byte [] b)方法,從而可以訪問并修改那個私有數組。注意,缺省的序列化并沒有把私有字節數組域暴露給DataInput/DataOutput字節數組方法。 應該使類不可被解除序列化。即使類不可被序列化,它依然可以被解除序列化。攻擊者可以構建一個字節序列,使它碰巧是被解除序列化的某個類實例,而且具有攻擊者選定的值。換句化話說,解除序列化是一種公共的構建函數,允許攻擊者選擇對象的狀態 -- 顯然是一個危險的操作! 要防止這一點,需要在類里增加如下方法: private final void readObject(ObjectInputStream in) throws java.io.IOException { throw new java.io.IOException("Class cannot be deserialized"); } 不要通過名稱來比較類。畢竟攻擊者可以用相同的名稱定義類,而且一不小心就會授予這些類不恰當的權限。因此,下面是一個判斷某個對象是否含有某個給定類的錯誤方法的例子: if (obj.getClass().getName().equals("Foo")) { 如果要判斷兩個對象是否含有完全相同的類,不要對雙方使用getClass()并使用“==”操作符進行比較,而應該使用如下形式: if (a.getClass() == b.getClass()) { 如果確實需要判斷某個對象是否含有某個給定類名,需要嚴格按照規范并確保使用當前名稱空間(當前類的ClassLoader所在名稱空間)。因此,應該使用如下形式: if (obj.getClass() == this.getClassLoader().loadClass("Foo")) { 本原則來自McGraw和Felten,而且確實是個好原則。要補充的是,盡可能地避免比較類值通常是個好注意。通常最好是盡力設計類的方法和接口,從而完全不必要做這些事。盡管如此,實際上無法完全做到,所以知道這些技巧還是很重要的。 不要把秘密(密鑰、密碼或算法)存儲在代碼或數據里。有惡意的JVM可以迅速看到這一數據。打亂代碼并不能在認真的攻擊者面前實際隱藏代碼。 |
溫馨提示:喜歡本站的話,請收藏一下本站!