java安全編碼指南之:敏感類的拷貝

簡介

一般來說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

本文已收錄於 //www.flydean.com/java-security-code-line-object-copy/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!