天天看點

《Java編碼指南:編寫安全可靠程式的75條建議》—— 指南1:限制敏感資料的生命周期

本節書摘來異步社群《java編碼指南:編寫安全可靠程式的75條建議》一書中的第1章,第1.1節,作者:【美】fred long(弗雷德•朗), dhruv mohindra(德魯•莫欣達), robert c.seacord(羅伯特 c.西科德), dean f.sutherland(迪恩 f.薩瑟蘭), david svoboda(大衛•斯沃博達),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

記憶體中的敏感資訊很容易受到攻擊,導緻洩漏。對于以下條件,不管應用程式滿足哪一個,能在應用程式所在的系統上執行代碼的攻擊者,都能通路這些資料。

使用對象來存儲敏感資料,但是内容在使用後沒有被清除或沒有被垃圾收集器回收。

具有可以被作業系統按需(例如,為了執行記憶體管理任務或者為了支援系統休眠)交換到磁盤的記憶體頁面。

在緩沖區(如bufferedreader)中有敏感資料。這些緩沖區持有敏感資料在作業系統緩存或記憶體中的副本。

使用了一些反射機制來控制敏感資料的流動,這些反射技巧可能規避掉系統對該資料的生命周期限制。

通過調試資訊、日志檔案、環境變量、線程轉儲和核心轉儲等方式暴露敏感資料。

如果記憶體中包含的敏感資料在使用後沒有及時被清除,那麼這些資料将極有可能被洩露。為了降低敏感資料洩露的風險,程式必須盡可能地最小化敏感資料的生命周期。

完全緩解(即對記憶體資料萬無一失的保護)需要底層作業系統和java虛拟機的支援。例如,如果将敏感資料交換至磁盤是一個問題,那麼就需要有一個禁用交換和休眠的安全作業系統。

違規代碼示例

在以下違規代碼示例中,程式從控制台讀取使用者名和密碼資訊,并将密碼存儲在一個string對象中。在垃圾收集器回收這個string對象關聯的記憶體之前,憑證資訊會一直處于暴露狀态。

class password {

 public static void main (string args[]) throws ioexception {

  console c = system.console();

  if (c == null) {

   system.err.println("no console.");

   system.exit(1);

  }

  string username = c.readline("enter your user name: ");

  char[] password = c.readpassword("enter your password: ");

  if (!verify(username, password)) {

   throw new securityexception("invalid credentials");

  // clear the password

  arrays.fill(password, ' ');

 }

 // dummy verify method, always returns true

 private static final boolean verify(string username,

   char[] password) {

  return true;

}<code>`</code>

console.readpassword()方法允許密碼以字元序列的形式傳回,而不是以string對象的形式。是以,程式員可以在使用密碼後立即将其從數組中清除。同時這個方法也禁止将密碼輸出到控制台。

下面的違規代碼示例使用了一個bufferedreader來包裝inputstreamreader對象,導緻敏感資料可以從檔案中被讀取。

void readdata(){

 bytebuffer buffer = bytebuffer.allocatedirect(16 * 1024);

 try (filechannel rdr =

    (new fileinputstream("file")).getchannel()) {

  while (rdr.read(buffer) &gt; 0) {

   // do something with the buffer

   buffer.clear();

 } catch (throwable e) {

  // handle error

注意,必須手動清除緩沖資料,因為垃圾收集器不會回收直接緩沖區。

适用性

對敏感資料生命周期限制失敗,導緻資訊洩露。

本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。