簡介
一般來說class中如果包含了私有的或者敏感的資料的時候是不允許被拷貝的。
如果一個class不想被拷貝,我們是不是不提供拷貝的方法就能保證class的安全了呢?
一起來看看吧。
一個簡單的SensitiveObject
假如我們有下面的一個SensitiveObject,它的作用就是儲存一個password,並且提供了一個修改password的方法:
public class SensitiveObject1 {
private char[] password;
SensitiveObject1(String iniValue){
this.password = iniValue.toCharArray();
}
public final String get() {
return String.valueOf(password);
}
public final void doPasswordChange(){
for(int i = 0; i < password.length; i++) {
password[i]= '*' ;}
}
public final void printValue(){
System.out.println(String.valueOf(password));
}
}
看上去沒什麼問題,如果我們希望密碼被返回之後就不能夠被修改,應該怎麼做呢?
SensitiveObject的限制
為了實現上面的功能,我們可以考慮引入一個是否返回的變數,如果返回過了,就不允許進行密碼修改了。
那麼我們可以將上面的程式碼修改成這樣:
public class SensitiveObject2 {
private char[] password;
private boolean returned=false;
SensitiveObject2(String iniValue){
this.password = iniValue.toCharArray();
}
public final String get()
{
if(!returned) {
returned=true;
return String.valueOf(password);
}else {
throw new IllegalStateException("已經返回過了,無法重複返回");
}
}
public final void doPasswordChange(){
if(!returned) {
for (int i = 0; i < password.length; i++) {
password[i] = '*';
}
}
}
}
通過加入了returned標籤,我們可以控doPasswordChange方法,只能在未返回之前進行密碼修改。
我們看下呼叫程式碼:
SensitiveObject2 sensitiveObject2= new SensitiveObject2("www.flydean.com");
sensitiveObject2.doPasswordEncrypt();
System.out.println(sensitiveObject2.get());
對SensitiveObject的攻擊
怎麼對上述程式碼進行攻擊呢?
如果我們想在密碼返回之後仍然對密碼進行修改,怎麼做到呢?
如果SensitiveObject2可以拷貝,我們是不是就能夠儲存一份char[]和boolean的副本了呢?
因為char[]屬於引用拷貝,所以在拷貝的副本里面對char[]進行修改完全可以影響到原SensitiveObject2的內容。
但是,雖然clone方法是定義在Object中的,如果子類沒有實現Cloneable介面的話,將會丟擲CloneNotSupportedException異常。
考慮到SensitiveObject2不是一個final的類,我們可以通過繼承SensitiveObject2來實現目的:
public class MaliciousSubSensitiveObject extends SensitiveObject2 implements Cloneable{
MaliciousSubSensitiveObject(String iniValue) {
super(iniValue);
}
public MaliciousSubSensitiveObject clone(){
MaliciousSubSensitiveObject s = null;
try {
s = (MaliciousSubSensitiveObject)super.clone();
} catch(Exception e) {
System.out.println("not cloneable");
}
return s;
}
public static void main(String[] args) {
MaliciousSubSensitiveObject object1 = new MaliciousSubSensitiveObject("www.flydean.com");
MaliciousSubSensitiveObject object2 = object1.clone();
String password1= object1.get();
System.out.println(password1);
object2.doPasswordChange();
object1.printValue();
}
}
可以看到,雖然object1先返回了password,但是這個password被clone過的object2進行了修改,最終導致object1中的password值發生了變化。
解決辦法
怎麼解決呢?
一個簡單的方法就是將SensitiveObject class定義為final,這樣就不能繼承,從而避免了上訴問題。
本文的例子:
learn-java-base-9-to-20/tree/master/security
本文已收錄於 http://www.flydean.com/java-security-code-line-object-copy/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!