Java 是一門安全的語言 (safe language),它對於指標及記憶體的管理上,相較於 C/C++ 容易許多。即使在安全的語言中,有時仍須採取保護手段,假設 class 的調用者會不計代價地破壞該 class 的約束規則,所以開發者在必要時需作出保護性的設計,確保在被破壞的情況下,依然正常運作。
Period 為 final 類,裡面的兩個 Date 也設定為 final,沒有 setter,好像不會有問題。
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(start + " after " + end);
this.start = start;
this.end = end;
}
public Date start() { return start; }
public Date end() { return end; }
}
其實將 start 與 end 傳入 Period 後,再對 end 作修改,這樣狀態就被改變了,從 Java 8 開始,Date 已漸漸由 Instant 取代。要保護 Period 實體避免受到這種攻擊,對於 constructor 的每個可變參數進行保護性拷貝 (defensive copy) 是必要的。Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // Modifies internals of p!
先進行保護性拷貝,再進行參數有效性的檢查,檢查的對象是拷貝後的對象,不是針對原始對象檢查。這樣是想要避免參數在檢查有效性時,被其它 thread 修改的機會。拷貝沒有選擇 clone,反而重新生成實體的原因為,對於有機會被繼承的 class (non-final class),攻擊者有可能繼承後覆寫 clone,你無法確定 clone 回傳的是否為標準 class 或是被攻擊者實作的 subclass。
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(start + " after " + end);
this.start = new Date(start);
this.end = new Date(end);
}
public Date start() { return new Date(start); }
public Date end() { return new Date(end); }
}
TOCTOU attack什麼時候需要保護性拷貝? 如果物件的內部資料中有可變類存在,且一旦客戶修改該實體後,會破壞邏輯,這時就應該在 constructor 中對傳入參數使用保護性拷貝。另外就是在回傳可變類的方法時,為避免讓客戶拿到可變類,所以必須要作保護性拷貝。如果客戶端可以保證不會修改可變類,或是即使可變類被修改也不會影響邏輯,那就不需要保護性拷貝。
https://winsys88.wordpress.com/2011/08/26/檢查時間使用時間(time-of-check-toc-time-of-use-tou)/
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2021/03/effective-java-50-defensive-copy.html
沒有留言:
張貼留言