序列化

雨晨漫步發表於2023-04-17

      記憶體中的資料物件只有轉換為二進位制流才可以進行資料持久化和網路傳輸。將資料物件轉換為二進位制流的過程成為物件的序列化(Serialization)。反之,將二進位制流恢復為資料物件的過程稱為反序列化(Deserialization)。序列化需要保留充分的資訊以恢復資料物件,但是為了節約儲存空間和網路頻寬,序列化後的二進位制流又要儘可能小。序列化常見的場景是RPC框架的資料傳輸。常見的序列化有三種:
      (1) Java原生序列化。Java類透過實現Serialization介面來是實現該類物件的序列化,這個介面非常特殊,沒有任何方法,只起標識作用。java序列化保留了物件類的後設資料(如類、成員變數、繼承類資訊),以及物件資料等,相容性最好,但不支援跨語言,而且效能一般。
      實現Serializable介面的類建議設定serivalVersionUID欄位值,如果不設定,那麼每次執行時,編譯器會根據類的內部實現,包括類名、介面名、方法和屬性等來自動生成的serialVersionUID。如果類的原始碼有修改,那麼重新編譯後serialVersionUID的取值可能會發生變化。因此實現Serializable介面的類一定要顯式地定義serialVersionUID屬性值。修改類時需要根據相容性決定是否修改serialVersion:
      · 如果是相容性升級,請不要修改serivalVersionUID欄位,避免反序列失敗。
      · 如果不是相容性升級,需要修改serivalVersionUID欄位,避免反序列混亂。
    使用JAVA原生序列化需注意,Java反序列化不會呼叫類的無參構造方法,而是呼叫native方法將成員變數賦值為對應型別的初始值。基於效能及相容性考慮,不推薦使用JAVA原生序列化。
      (2) Hessian序列化。 Hessian序列化是一種支援動態型別、跨語言、基於物件傳輸的網路協議。JAVA物件序列化的二進位制流可以被其他語言(如C++ 、Python )反序列化。Hessian協議具有如下特性:
      · 自描述序列化型別。不依賴外部描述檔案或介面定義,用一個位元組表示常用基礎型別,極大縮短二進位制流。
      · 語言無關,支援指令碼語言。
      · 協議簡單,比JAVA原生序列化高效。
      相比Hessian 1.0,Hessian 2.0中增加了壓縮編碼,其序列化二進位制流大小是JAVA序列化的50%,序列化耗時是JAVA序列化的30%,反序列化耗時是JAVA反序列化的20%。
   Hessian會把複雜物件所有屬性儲存在一個MAP中進行序列化。所以在父類、在類存在同名成員變數的情況下,Hessian序列化時,先序列化子類,然後序列化父類,因此序列化結果會導致子類同名成員變數被父類的值覆蓋。
      (3) JSON序列化。 JSON(JavaScript Object Notation)是一種輕量級的資料互動格式。JSON序列化就是將資料物件轉換為JSON字串。在序列化過程中拋棄了型別資訊,所以反序列化時只是提供型別資訊才能準確地反序列化。相比前兩者方式,JSON可讀性比較好,方便除錯。
    序列化通常會透過網路傳輸物件,而物件中往往有敏感資料,所以序列化常常稱為駭客的攻擊點,攻擊者巧妙地利用反序列過程構造惡意程式碼,使得程式在反序列化的過程中執行任意程式碼。JAVA工程中廣泛使用的Apache Commons Collections、Jackson、fastjson等都出現過反序列化漏洞。如何防範這種駭客攻擊呢?有些物件的敏感屬性不需要進行序列化傳輸,可以加transient關鍵字,避免把此屬性資訊轉化為序列化的二進位制流。如果一定要傳遞物件的敏感屬性,可以使用對稱與非對稱加密方式獨立傳輸,再使用某個方法把屬性還原到物件中。應用開發者對序列化要有一定的安全防範意識,對傳入資料的內容進行校驗或許可權控制,及時更新安全漏洞,避免受到攻擊。

相關文章