JavaRush /Java Blog /Random-TW /Java 安全性:最佳實踐
Roman Beekeeper
等級 35

Java 安全性:最佳實踐

在 Random-TW 群組發布
在伺服器應用中,最重要的指標之一就是安全性。這是非功能性需求的類型之一。 Java 安全性:最佳實務 - 1安全性有很多組成部分。當然,為了完全涵蓋所有已知的保護原則和行動,您需要寫不只一篇文章,所以讓我們專注於最重要的。任何團隊都需要一個精通該主題的人,能夠設定所有流程並確保它們不會造成新的安全漏洞。當然,您不應該認為如果遵循這些做法,應用程式就會完全安全。不!但和他們在一起肯定會更安全。去。

1.提供Java語言等級的安全性

首先,Java 的安全性是從語言功能層級開始的。如果沒有存取修飾符,這就是我們會做的事情?...無政府狀態,同樣如此。程式語言可以幫助我們編寫安全程式碼並利用許多隱式安全功能:
  1. 強打字。Java是一種靜態類型語言,能夠在執行時間偵測類型錯誤。
  2. 存取修飾符。感謝他們,我們可以按照我們需要的方式配置對類別、方法和類別欄位的存取。
  3. 自動記憶體管理。為此,我們(Javaists ;))提供了垃圾收集器,它使您無需手動設定。是的,有時會出現問題。
  4. 字節碼檢查: Java 編譯為字節碼,在運行之前由運行時檢查。
除此之外,Oracle也提出了有關安全性的建議。當然,它寫得併不“高調”,你可能會讀著讀著睡著好幾次,但這是值得的。一個特別重要的文件是《Java SE 安全編碼指南》,它提供了有關如何編寫安全程式碼的提示。該文件包含許多有用的信息。如果可能的話,這絕對值得一讀。為了激發人們對這些材料的興趣,這裡有一些有趣的提示:
  1. 避免序列化安全敏感類。在這種情況下,您可以從序列化檔案中取得類別接口,更不用說正在序列化的資料了。
  2. 盡量避免可變資料類。這提供了不可變類的所有好處(例如線程安全)。如果存在可變對象,這可能會導致意外的行為。
  3. 複製傳回的可變物件。如果方法傳回內部可變物件的引用,則用戶端程式碼可以變更該物件的內部狀態。
  4. 等等…
一般來說,Java SE 安全編碼指南包含一組有關如何正確、安全地用 Java 編寫程式碼的提示和技巧。

2.消除SQL注入漏洞

獨特的脆弱性。它的獨特之處在於它既是最著名的漏洞之一,也是最常見的漏洞之一。如果您對安全問題不感興趣,那麼您就不會了解它。什麼是SQL注入?這是透過在不期望的地方注入額外的 SQL 程式碼來對資料庫進行的攻擊。假設我們有一個方法,它需要一些參數來查詢資料庫。例如,用戶名。存在漏洞的程式碼如下所示:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Пишем sql request в базу данных с нашим firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // выполняем request
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводит ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводит в коллекцию юзеров
}
在此範例中,sql 查詢是在單獨的行中預先準備好的。看來問題是什麼,對吧?也許問題是使用會更好String.format?不?然後怎樣呢?讓我們把自己放在測試人員的位置上,思考這個數值能夠傳達什麼內容firstName。例如:
  1. 您可以傳遞預期的內容 - 使用者名稱。然後資料庫將傳回具有該名稱的所有使用者。
  2. 您可以傳遞一個空字串:然後將傳回所有使用者。
  3. 或者您可以傳遞以下內容:「''; 刪除表用戶;」。這裡將會出現更大的問題。此查詢將從資料庫中刪除該表。有了所有的數據。每個人。
你能想像這會導致什麼問題嗎?然後你就可以寫任何你想要的東西。您可以更改所有使用者的名稱,也可以刪除他們的地址。破壞的範圍是巨大的。為了避免這種情況,您需要停止注入現成的查詢,而是使用參數建立它。這應該是查詢資料庫的唯一方法。這樣就可以消除這個漏洞。例子:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Создаем параметризированный request.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Создаем подготовленный стейтмент с параметризованным requestом
   PreparedStatement statement = connection.prepareStatement(query);

   // Передаем meaning параметра
   statement.setString(1, firstName);

   // выполняем request
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводим ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводим в коллекцию юзеров
}
這樣就可以避免這個漏洞。對於那些想要比本文更深入研究這個問題的人,這裡有一個很好的例子。你怎麼知道你是否理解了這一部分?如果下面的笑話變得清晰,那麼這肯定表明該漏洞的本質已經清楚:D Java 安全性:最佳實務 - 2

3.掃描並保持依賴更新

這是什麼意思?對於那些不知道什麼是依賴項的人,我將解釋一下:它是一個包含程式碼的 jar 存檔,使用自動建置系統(Maven、Gradle、Ant)連接到項目,以便重複使用其他人的解決方案。例如Project Lombok,它在運行時為我們產生 getter、setter 等。如果我們談論大型應用程序,它們會使用許多不同的依賴項。有些是可傳遞的(即每個依賴項都可以有自己的依賴項,等等)。因此,攻擊者越來越關注開源依賴項,因為它們經常使用並且可能會給許多客戶端帶來問題。重要的是要確保整個依賴關係樹中不存在已知的漏洞(這正是它看起來的樣子)。有幾種方法可以做到這一點。

使用Snyk進行監控

Snyk工具檢查所有專案依賴性並標記已知漏洞。例如,您可以在那裡透過 GitHub 註冊和匯入您的專案。 Java 安全性:最佳實務 - 3另外,從上圖可以看出,如果新版本有針對此漏洞的解決方案,Snyk 將提供解決方案並建立 Pull-Request。它可以免費用於開源專案。項目將以某種頻率進行掃描:每週一次、每月一次。我註冊了所有公共儲存庫並將其添加到 Snyk 掃描中(這沒有危險:它們已經向所有人開放)。接下來,Snyk 顯示了掃描結果: Java 安全性:最佳實務 - 4過了一會兒,Snyk-bot 在需要更新依賴項的專案中準備了多個 Pull-Request: Java 安全性:最佳實務 - 5這是另一個: Java 安全性:最佳實務 - 6所以這是一個用於搜尋漏洞和監控更新的優秀工具新版本。

使用 GitHub 安全實驗室

在 GitHub 上工作的人也可以利用他們的內建工具。您可以在我的翻譯中從他們的部落格GitHub 安全實驗室公告 中閱讀有關此方法的更多資訊。當然,這個工具比 Snyk 更簡單,但你絕對不應該忽略它。另外,已知漏洞的數量只會增加,因此 Snyk 和 GitHub 安全實驗室都將擴大和改進。

激活 Sonatype DepShield

如果您使用 GitHub 儲存儲存庫,則可以從 MarketPlace - Sonatype DepShield 將應用程式之一新增至您的專案。在它的幫助下,您還可以掃描項目的依賴關係。此外,如果發現某些內容,將建立一個 GitHub Issue 並帶有相應的描述,如下所示: Java 安全性:最佳實務 - 7

4. 謹慎處理敏感數據

Java 安全性:最佳實務 - 8在英語演講中,「敏感資料」一詞更為常見。洩露客戶的個人資訊、信用卡號碼和其他個人資訊可能會造成無法彌補的損害。首先,您需要仔細查看應用程式設計並確定是否確實需要任何資料。也許其中一些是不需要的,但它們是為了一個尚未到來且不太可能到來的未來而添加的。此外,在專案記錄過程中,此類資料可能會洩漏。防止敏感資料進入日誌的一個簡單方法是清理toString()網域實體(例如使用者、學生、教師等)的方法。這將防止敏感字段被意外列印。如果使用 Lombok 產生方法toString(),則可以使用註解@ToString.Exclude來防止透過該方法在輸出中使用該欄位toString()。此外,與外界分享資料時要非常小心。例如,有一個 http 端點顯示所有使用者的名稱。無需顯示使用者的內部唯一ID。為什麼?因為使用它,攻擊者可以獲得有關每個用戶的其他更機密的資訊。例如,如果您使用 Jackson 將POJO序列化和反序列化為JSON,則可以使用@JsonIgnore和註釋@JsonIgnoreProperties來防止特定欄位被序列化和反序列化。一般來說,不同的地方需要使用不同的POJO類別。這是什麼意思?
  1. 若要使用資料庫,請僅使用 POJO - 實體。
  2. 若要使用業務邏輯,請將實體傳輸到模型。
  3. 若要與外界合作並發送 http 請求,請使用第三方實體 - DTO。
這樣您就可以清楚定義哪些欄位從外部可見,哪些欄位不可見。

使用強大的加密和雜湊演算法

機密客戶資料必須安全儲存。為此,您需要使用加密。根據任務,您需要決定使用哪種類型的加密。此外,更強的加密需要更多時間,因此您需要再次考慮對它的需求在多大程度上證明在其上花費的時間是合理的。當然,你可以自己寫演算法。但這是不必要的。您可以利用該領域的現有解決方案。例如,Google Tink
<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupId>com.google.crypto.tink</groupId>
   <artifactId>tink</artifactId>
   <version>1.3.0</version>
</dependency>
讓我們看看如何使用它,使用如何加密一種方式和另一種方式的範例:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Цой жив!";
   String aad = "Юрий Клинских";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

密碼加密

對於此任務,使用非對稱加密是最安全的。為什麼?因為應用程式確實不需要解密回密碼。這是一般方法。實際上,當使用者輸入密碼時,系統會對其進行加密並將其與密碼庫中的內容進行比較。加密是使用相同的方式進行的,因此您可以期望它們匹配(當然,如果您輸入正確的密碼;)。BCrypt 和 SCrypt 適合此目的。兩者都是單向函數(加密雜湊),具有計算複雜的演算法,需要花費大量時間。這正是你所需要的,因為正面破解它需要很長時間。例如,Spring Security 支援一系列演算法。SCryptPasswordEncoder您也可以使用BCryptPasswordEncoder. 現在強大的加密演算法明年可能會變得很弱。因此,我們得出的結論是,有必要檢查所使用的演算法並更新演算法庫。

而不是輸出

今天我們談到了安全,當然,很多事情都被拋在後面了。我剛剛為你打開了新世界的大門:一個擁有自己生命的世界。安全問題和政治議題一樣:你不參與政治,政治就會參與你。傳統上,我建議訂閱我的 Github 帳戶。在那裡,我發布了我在工作中學習和應用的各種技術的作品。

有用的連結

是的,該網站上幾乎所有文章都是用英文寫的。無論我們喜歡與否,英語都是程式設計師溝通的語言。所有有關程式設計的最新文章、書籍和雜誌都是用英語寫的。這就是為什麼我的推薦連結大多是英文的:
  1. Habr:針對初學者的 SQL 注入
  2. Oracle:Java 安全資源中心
  3. Oracle:Java SE 安全編碼指南
  4. Baeldung:Java 安全基礎知識
  5. 中:增強 Java 安全性的 10 個技巧
  6. Snyk:10 個 Java 安全最佳實踐
  7. JR:GitHub 安全實驗室公告:共同保護您的所有程式碼
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION