您的 Java 程式碼安全嗎?
雖然客戶仍然很關心您為他們構建的應用程式的可伸縮性和可用性,但他們可能變得也很關心安全性,而且要求特別嚴格。應用程式可能容易受到兩類安全性威脅的攻擊:靜態和動態。雖然開發人員不能完全控制動態威脅,但在開發應用程式時,您可以採取一些預防措施來消除靜態威脅。本文概括並解釋了 13 種型別的靜態暴露 ― 它們是系統中的缺陷,它使系統暴露在想要篡奪該系統的特權的攻擊者面前。您將學會如何處理這些暴露,以及如何發現(如果不處理這些暴露)這些暴露可能造成的影響。 在開發 Java Web 應用程式時,您需要確保應用程式擁有完善的安全性特徵補充。這裡在談到 Java 安全性時,我們並不談及 Java 語言提供的安全性 API,也不涉及使用 Java 程式碼來保護應用程式。本文將著重討論可能潛伏在您的 Java 應用程式中的 安全性暴露。安全性暴露是系統中的缺陷,它使系統無法 ― 即使系統被正常使用 ― 防止攻擊者篡奪對系統的特權、控制系統的執行、危及系統上的資料安全或者假冒未經授權的信任。相對於安全性暴露,許多開發人員更加關心網站的感官效果。 毫無疑問,客戶現在既嚴格地關注效能、可伸縮性和可用性也嚴格地關注安全性。應用程式可能容易受到兩類安全性威脅的攻擊: 動態和 靜態。動態威脅是那些同未經授權進入系統有關的威脅,或那些同跨越網路傳輸的資料的完整性、隱私和機密性有關的威脅。這些威脅同應用程式的功能程式碼沒有多大關係;使用加密、加密術和認證技術來消除這些威脅。相比之下,靜態威脅卻同應用程式的功能程式碼 有關;它們同進入系統的授權使用者所做的事情有關。未知使用者闖入系統是動態威脅的一個示例;授權使用者以未授權方式作業系統內的程式碼或資料是靜態威脅的示例。應用程式開發人員並不能完全控制動態威脅;但開發人員在構建應用程式時卻可以採取預防措施來消除靜態威脅。 在本文中,我們討論了對付 13 種不同靜態暴露的技巧。對於每種暴露,我們解釋了不處理這些安全性問題所造成的影響。我們還為您推薦了一些準則,要開發不受這些靜態安全性暴露威脅的、健壯且安全的 Java 應用程式,您應該遵循這些準則。一有合適的時機,我們就提供程式碼樣本(既有暴露的程式碼也有無暴露的程式碼)。 對付高嚴重性暴露的技巧 請遵循下列建議以避免高嚴重性靜態安全性暴露: ·限制對變數的訪問 限制對變數的訪問 如果將變數宣告為 public,那麼外部程式碼就可以操作該變數。這可能會導致安全性暴露。 影響 清單 1 演示了帶有 public 變數的程式碼,因為變數為 public 的,所以它暴露了。 清單 1. 帶有 public 變數的程式碼 class Test { Test(){ public class MyClass extends Test{ public static void main(String[] args){ 建議 清單 2. 不帶有 public 變數的程式碼 class Test { Test(){ 讓每個類和方法都為 final 不允許擴充套件的類和方法應該宣告為 final 。這樣做防止了系統外的程式碼擴充套件類並修改類的行為。 影響 建議 不要依賴包作用域 影響 建議 使類不可克隆 克隆允許繞過構造器而輕易地複製類例項。 影響 清單 3. 可克隆程式碼 class MyClass{ private int id; public MyClass(){ public MyClass(int id,String name){ public void display(){ public class Hacker extends MyClass implements Cloneable { public static void main(String[] args){ 建議 清單 4. 使您的程式碼不可克隆 public final Object clone() throw new java.lang.CloneNotSupportedException(); 如果想讓您的類可克隆並且您已經考慮了這一選擇的後果,那麼您仍然可以保護您的類。要做到這一點,請在您的類中定義一個為 final 的克隆方法,並讓它依賴於您的一個超類中的一個非 final 克隆方法,如清單 5 中所示: 清單 5. 以安全的方式使您的程式碼可克隆 public final Object clone() super.clone(); 類中出現 clone() 方法防止攻擊者重新定義您的 clone 方法。 使類不可序列化 序列化允許將類例項中的資料儲存在外部檔案中。闖入程式碼可以克隆或複製例項,然後對它進行序列化。 影響 建議 清單 6. 防止物件序列化 private final void writeObject(ObjectOutputStream out) throw new java.io.NotSerializableException("This object cannot 通過將 writeObject() 方法宣告為 final,防止了攻擊者覆蓋該方法。 使類不可逆序列化 通過使用逆序列化,攻擊者可以用外部資料或位元組流來例項化類。 影響 建議 清單 7. 防止物件逆序列化 private final void readObject(ObjectInputStream in) throw new java.io.NotSerializableException("This object cannot 通過將該方法宣告為 final ,防止了攻擊者覆蓋該方法。 避免硬編碼敏感資料 您可能會嘗試將諸如加密金鑰之類的祕密存放在您的應用程式或庫的程式碼。對於你們開發人員來說,這樣做通常會把事情變得更簡單。 影響 建議 這一問題的一種可能解決方案是:將敏感資料儲存在屬性檔案中,無論什麼時候需要這些資料,都可以從該檔案讀取。如果資料極其敏感,那麼在訪問屬性檔案時,您的應用程式應該使用一些加密/解密技術。 查詢惡意程式碼 從事某個專案的某個心懷叵測的開發人員可能故意引入易受攻擊的程式碼,打算日後利用它。這樣的程式碼在初始化時可能會啟動一個後臺程式,該程式可以為闖入者開後門。它也可以更改一些敏感資料。 這樣的惡意程式碼有三類: ·類中的 main 方法 影響 請檢查程式碼中是否有未使用的方法出現。這些方法在測試期間將會通過所有的安全檢查,因為在程式碼中不呼叫它們 ― 但它們可能含有硬編碼在它們內部的敏感資料(雖然是測試資料)。引入一小段程式碼的攻擊者隨後可能呼叫這樣的方法。 避免最終應用程式中的死程式碼(註釋內的程式碼)。如果闖入者去掉了對這樣的程式碼的註釋,那麼程式碼可能會影響系統的功能性。 可以在清單 8 中看到所有三種型別的惡意程式碼的示例: 清單 8. 潛在惡意的 Java 程式碼 public void unusedMethod(){ public void usedMethod(){ 建議 對付中等嚴重性暴露的技巧 請遵循下列建議以避免中等嚴重性靜態安全性暴露: ·不要依賴初始化 不要依賴初始化 您可以不執行構造器而分配物件。這些物件使用起來不安全,因為它們不是通過構造器初始化的。 影響 例如,請想象為客戶建立新帳戶的 Account 物件。只有在 Account 期初餘額大於 0 時,才可以開設新帳戶。可以在構造器裡執行這樣的驗證。有些人未執行構造器而建立 Account 物件,他可能建立了一個具有一些負值的新帳戶,這樣會使系統不一致,容易受到進一步的干預。 建議 清單 9. 使用布林標誌以檢查初始化過程 public class MyClass{ private boolean initialized = false; public MyClass (){ private void method1(){ //no need to check for initialization variable public void method2(){ else{ 如果物件由逆序列化進行初始化,那麼上面討論的驗證機制將難以奏效,因為在該過程中並不呼叫構造器。在這種情況下,類應該實現 ObjectInputValidation 介面: 清單 10. 實現 ObjectInputValidation interface java.io.ObjectInputValidation { 所有驗證都應該在 validateObject() 方法中執行。物件還必須呼叫 ObjectInputStream.RegisterValidation() 方法以為逆序列化物件之後的驗證進行註冊。 RegisterValidation() 的第一個引數是實現 validateObject() 的物件,通常是對物件自身的引用。注:任何實現 validateObject() 的物件都可能充當物件驗證器,但物件通常驗證它自己對其它物件的引用。 RegisterValidation() 的第二個引數是一個確定回撥順序的整數優先順序,優先順序數字大的比優先順序數字小的先回撥。同一優先順序內的回撥順序則不確定。 當物件已逆序列化時, ObjectInputStream 按照從高到低的優先順序順序呼叫每個已註冊物件上的 validateObject() 。 不要通過名稱來比較類 有時候,您可能需要比較兩個物件的類,以確定它們是否相同;或者,您可能想看看某個物件是否是某個特定類的例項。因為 JVM 可能包括多個具有相同名稱的類(具有相同名稱但卻在不同包內的類),所以您不應該根據名稱來比較類。 影響 例如,請假設您想確定某個物件是否是類 com.bar.Foo 的例項。清單 11 演示了完成這一任務的錯誤方法: 清單 11. 比較類的錯誤方法 if(obj.getClass().getName().equals("Foo")) // Wrong! 建議 清單 12. 比較類的更好方法 if(obj.getClass() == this.getClassLoader().loadClass("com.bar.Foo")){ 然而,比較類的更好方法是直接比較類物件看它們是否相等。例如,如果您想確定兩個物件 a 和 b 是否屬同一個類,那麼您就應該使用清單 13 中的程式碼: 清單 13. 直接比較物件來看它們是否相等 if(a.getClass() == b.getClass()){ 儘可能少用直接名稱比較。 不要使用內部類 Java 位元組碼沒有內部類的概念,因為編譯器將內部類轉換成了普通類,而如果沒有將內部類宣告為 private ,則同一個包內的任何程式碼恰好能訪問該普通類。 影響 建議 對付低嚴重性暴露的技巧 請遵循下列建議以避免低嚴重性靜態安全性暴露: ·避免返回可變物件 避免返回可變物件 Java 方法返回物件引用的副本。如果實際物件是可改變的,那麼使用這樣一個引用呼叫程式可能會改變它的內容,通常這是我們所不希望見到的。 影響 清單 14 演示了脆弱性。 getExposedObj() 方法返回了 Exposed 物件的 引用副本,該物件是可變的: 清單 14. 返回可變物件的引用副本 class Exposed{ public Exposed(){ public class Exp12{ public Exposed getExposedObj(){ } 建議 清單 15. 返回可變物件的副本 public Exposed getExposedObj(){ 或者,您的程式碼也可以返回 Exposed 物件的克隆。 檢查本機方法 本機方法是一種 Java 方法,其實現是用另一種程式語言編寫的,如 C 或 C++。有些開發人員實現本機方法,這是因為 Java 語言即使使用即時(just-in-time)編譯器也比許多編譯過的語言要慢。其它人需要使用本機程式碼是為了在 JVM 以外實現特定於平臺的功能。 影響 建議 ·它們返回什麼 結束語 |
相關文章
- 本週 GitHub 速覽:您的程式碼有聲兒嗎?(Vol.38)Github
- 您的資料安全嗎?如何評估和降低資料風險
- Java老碼農心得:捲了這麼多年,您真的卷會了嗎?Java
- 使用htmlprettify美化您的HTML程式碼HTML
- 【開源社】您會為開源專案貢獻程式碼嗎?
- Mr Banq, 我是這樣做java的,您覺的正常嗎?或者說合理嗎?Java
- 你的密碼真的安全嗎?密碼
- 使用工具Source Monitor測量您Java程式碼的環複雜度Java複雜度
- 您真的瞭解Java中的鎖嗎?這7種不同維度下的鎖知道嗎?Java
- Quod AI:更快地找到您需要的程式碼AI
- 您的系統真的需要EJB嗎?
- 虛擬CISO(首席資訊保安官)是您的安全問題解決方案嗎?(上)
- 您的單例模式,真的單例嗎?單例模式
- 準備好提升您的ITSM了嗎?
- 去除冗餘 – 精簡您的CSS樣式程式碼CSS
- 年底了,你的資料庫密碼安全嗎資料庫密碼
- 你的程式碼有重複嗎?
- 網路安全裡面的程式碼審計難學嗎?如何學習?
- PHP 程式碼安全PHP
- 程式碼安全 兩種程式碼漏洞
- 新Web時代,您準備好了嗎?Web
- 你覺得我的這段Java程式碼還有優化的空間嗎?Java優化
- 低程式碼/無程式碼的SaaS/CRM還有未來嗎?
- 你會敲程式碼嗎
- Java程式碼加密支援Android App Bundle動態化框架,守護核心程式碼安全Java加密AndroidAPP框架
- 電視劇裡的程式碼真能執行嗎?
- 如何讓您的VFP程式更安全?對VFP程式設計師的一點忠告。 (1千字)程式設計師
- Java 中的程式碼塊Java
- Java中的程式碼塊Java
- 容器和容器映象的區別,您真的瞭解嗎
- 您的客戶是一個技術威脅嗎?
- 低程式碼+原生安全=?
- iOS安全/程式碼混淆iOS
- 如何讓自己的程式碼更加安全?
- Kotlin的空安全真的安全嗎?Kotlin
- 工作5年的Java程式設計師,才學會閱讀原始碼,可悲嗎?Java程式設計師原始碼
- 雲端計算如何讓您的企業更加安全?
- java是最值得學習的程式語言嗎?Java