JAVA序列化危機
Apache Commons Collection 中的反序列化漏洞在 2016 年撼動了整個Java 生態系統,也影響到了 70 餘個其他的 Java 庫,甚至還讓 PayPal 的服務器遭受影響。
OWASP組織將“不安全的反序列化”列爲2017年10項最嚴重的Web 應用程序安全風險榜的第8位。
Android 反序列化漏洞 CVE-2014-7911
Android <5.0系統中,可以利用ObjectInputStream未校驗是否可反序列化的漏洞,惡意傳入不可序列化對象將產生類型混淆,成員變量被當成指針向system_server進程注入代碼,由於system_server擁有system權限,從而使得注入的代碼以system權限執行。
序列化代理模式
由於使用序列化在整個行業的巨大風險,在java取消或使用新的替代品之前,最好對序列化行爲進行代理,可以阻止僞字節流***和內部域盜用***,避免造成重大損失。
java的隱藏方法
隱藏方法介紹:
readObject()和writeObject(),可以自定義序列化傳輸過程,例如在前後添加自定義內容,或者直接修改返回結果;可以在readObject添加保護性代碼,校驗是否真實結果。
writeReplace():實際序列化的對象將是作爲writeReplace方法返回值的對象,而且序列化過程的依據是實際被序列化對象的序列化實現。
readResolve():在類中有多個實例時,可以通過該方法去決定反序列化後的結果。
在Serializable接口定義中並無這些方法,實際是ObjectOutputStream使用了反射來尋找是否聲明瞭這些隱藏方法再進行調用。
使用隱藏方法實現序列化代理模式
參照Effective Java序列化代理模式如下:
public class Interval implements Serializable {
private final Date start;
private final Date end;
public Interval(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(start + " after " + end);
}
//提供序列化方法
private Object writeReplace() {
return new SerializationProxy(this);
}
//禁止反序列化外圍類
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required!");
}
public Date getStart() {return new Date(start.getTime());}
public Date getEnd() {return new Date(end.getTime());}
@Override
public String toString() {
return "Interval{" + "start=" + start + ", end=" + end + '}';
}
private static class SerializationProxy implements Serializable {
private static final long serivalVersionUID = 213214124141L;
private final Date start;
private final Date end;
SerializationProxy(Interval interval) {
this.start = interval.start;
this.end = interval.end;
}
//轉回外圍類
private Object readResolve() {
return new Interval(start, end);
}
}
}
注意應用場景和使用的缺陷
1 擴展性差,需要同步修改實例對象和代理對象。
2 如果直接使用代理類,無法調用對象方法,需要轉換成實際類使用。
3 安全但開銷更大。