CodeGym /Các khóa học /JAVA 25 SELF /Các vấn đề của tuần tự hóa nhị phân: bảo mật, khả năng tư...

Các vấn đề của tuần tự hóa nhị phân: bảo mật, khả năng tương thích

JAVA 25 SELF
Mức độ, Bài học
Có sẵn

1. Bảo mật của tuần tự hóa nhị phân

Tuần tự hóa trong Java không chỉ là lưu các trường của đối tượng. Đó là khả năng “khôi phục” bất kỳ đối tượng nào với bất kỳ nội dung nào, miễn là nó triển khai giao diện Serializable. Nghe có vẻ tiện lợi! Nhưng nếu ứng dụng của bạn giải tuần tự dữ liệu nhận từ nguồn không đáng tin (ví dụ từ mạng hoặc từ tệp có thể bị kẻ tấn công thay thế), nó có nguy cơ trở thành nạn nhân của các cuộc tấn công.

Nó hoạt động thế nào?

Trong quá trình giải tuần tự, Java tạo các đối tượng dựa trên luồng byte mà không gọi constructor của lớp. Nếu lớp triển khai các phương thức đặc biệt như readObject, chúng sẽ được gọi tự động. Kẻ tấn công có thể “chế” luồng byte sao cho khi giải tuần tự sẽ thực thi mã dễ bị khai thác.

Ví dụ: tấn công “gadget chain”

Hãy hình dung bạn có một lớp mà khi giải tuần tự sẽ chạy lệnh bên ngoài (đọc tệp hoặc gọi shell). Nếu kẻ tấn công biết ứng dụng giải tuần tự các đối tượng thuộc một số kiểu nhất định, hắn có thể cung cấp một luồng byte đặc biệt dẫn đến việc thực thi mã độc. Những cuộc tấn công như vậy được xây dựng thành “chuỗi gadget” — một chuỗi lời gọi kết thúc bằng thao tác nguy hiểm.

Vì sao điều này nghiêm trọng đến vậy?

Giải tuần tự là quá trình khôi phục các đối tượng thực từ luồng dữ liệu, và vào thời điểm đó có thể thực thi mã tùy ý. Nếu dữ liệu đến từ nguồn không tin cậy, điều này mở đường cho việc thực thi lệnh từ xa (RCE). Nhiều công ty lớn đã ban hành khuyến nghị từ bỏ giải tuần tự không an toàn; trong các dự án doanh nghiệp hiện đại, tuần tự hóa nhị phân thường bị cấm bởi chính sách bảo mật.

Bảo vệ thế nào?

  • Không bao giờ giải tuần tự các đối tượng từ nguồn không tin cậy.
  • Sử dụng whitelisting — danh sách rõ ràng các kiểu được phép giải tuần tự.
  • Ưu tiên các định dạng văn bản cho tích hợp bên ngoài: JSON, XML.
  • Nếu không thể tránh tuần tự hóa, hãy dùng các thư viện có cấu hình giải tuần tự an toàn (ví dụ, Jackson với giới hạn kiểu).
  • Hạn chế sử dụng các phương thức tuần tự hóa tùy chỉnh (readObject, readResolve, v.v.) nếu bạn không chắc về độ an toàn của chúng.

2. Khả năng tương thích giữa các phiên bản lớp

Tuần tự hóa nhị phân trong Java gắn chặt với cấu trúc của lớp. Bạn tuần tự hóa một đối tượng ở phiên bản 1.0, rồi cập nhật lớp (thêm/bỏ trường) — cố gắng giải tuần tự “đối tượng cũ” vào phiên bản mới có thể dẫn đến lỗi hoặc mất dữ liệu.

Java xác định khả năng tương thích như thế nào?

Để làm việc này, Java dùng một trường đặc biệt — serialVersionUID. Đây là định danh phiên bản của lớp. Nếu đối tượng đã tuần tự hóa có một serialVersionUID, còn lớp hiện tại có giá trị khác — sẽ ném InvalidClassException, và việc giải tuần tự sẽ không diễn ra.

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L; // chỉ định phiên bản một cách tường minh

    private String name;
    private int age;
}

Nếu bạn thay đổi cấu trúc lớp (ví dụ, thêm trường email) mà không thay đổi serialVersionUID, Java sẽ coi lớp là tương thích và cố gắng giải tuần tự đối tượng cũ. Còn nếu bạn không chỉ định serialVersionUID một cách tường minh, JVM sẽ sinh ra nó tự động dựa trên cấu trúc, và bất kỳ thay đổi nào cũng dẫn đến không tương thích.

Điều gì xảy ra khi phiên bản không khớp/khớp nhau?

Nếu các định danh không trùng nhau — việc giải tuần tự sẽ không diễn ra: InvalidClassException. Nếu trùng nhau — các trường được ánh xạ theo tên và kiểu: các trường mới nhận giá trị mặc định (null, 0), các trường đã xóa — bị bỏ qua. Khi thay đổi kiểu hoặc tên trường có thể phát sinh lỗi và diễn giải dữ liệu sai.

Lời khuyên thực tiễn. Trong các lớp có thể tuần tự hóa, hãy luôn đặt serialVersionUID một cách tường minh. Chỉ thay đổi nó khi có các thay đổi không tương thích (xóa/thay đổi kiểu của trường quan trọng). Khi chỉ thêm trường mới, có thể giữ nguyên định danh — JVM sẽ xử lý đúng các đối tượng cũ.

Bảng: Điều gì xảy ra khi thay đổi lớp

Thay đổi trong lớp Điều gì sẽ xảy ra khi giải tuần tự?
Thêm trường mới Nhận giá trị mặc định (0, null)
Xóa trường Bị bỏ qua khi đọc dữ liệu cũ
Thay đổi kiểu trường Ngoại lệ hoặc dữ liệu sai
Đổi tên trường Trường cũ bị bỏ qua, trường mới — giá trị mặc định
Thay đổi serialVersionUID Ngoại lệ InvalidClassException

3. Hạn chế của tuần tự hóa chuẩn

Không phải mọi đối tượng đều có thể tuần tự hóa

Các trường có modifier transient và static không được tuần tự hóa. static — vì thuộc về lớp chứ không phải đối tượng; transient — vì bạn đã chủ động cấm tuần tự hóa trường đó.

Một số đối tượng theo định nghĩa là không thể tuần tự hóa: Thread, các kết nối cơ sở dữ liệu, socket, Scanner, v.v. Nếu lớp của bạn có trường thuộc kiểu như vậy và nó không transient, bạn sẽ nhận NotSerializableException.

import java.io.Serializable;
import java.util.Scanner;

public class Session implements Serializable {
    private transient Scanner scanner; // không được tuần tự hóa!
    private String login;
}

Vấn đề về hiệu năng và khả năng mở rộng

Tuần tự hóa các đồ thị đối tượng lớn có thể chậm và tốn bộ nhớ.

Định dạng nhị phân không phù hợp cho tích hợp với các nền tảng và ngôn ngữ khác — chỉ Java “hiểu” nó.

Khó kiểm soát chính xác những gì được tuần tự hóa, đặc biệt với các hệ phân cấp sâu và tham chiếu vòng.

Vấn đề khi hỗ trợ dữ liệu cũ

Lưu trữ lâu dài các ảnh chụp nhị phân là rủi ro. Sau một hai năm, cấu trúc lớp thay đổi và các tệp cũ không còn tải được.

Câu chuyện có thật: “Chúng tôi đã lưu cache người dùng đã tuần tự hóa ba năm trước, cập nhật ứng dụng, và giờ không thể tải lại nó. Xin chào, dữ liệu bị mất!”

4. Best practices: làm sao tránh bẫy

  • Chỉ dùng tuần tự hóa nhị phân cho các tác vụ nội bộ, nơi bạn kiểm soát cả hai phía của quy trình.
  • Không dùng tuần tự hóa nhị phân cho tích hợp bên ngoài và lưu trữ lâu dài dữ liệu quan trọng.
  • Luôn chỉ định serialVersionUID một cách tường minh trong các lớp có thể tuần tự hóa.
  • Đánh dấu các trường không nên tuần tự hóa bằng modifier transient.
  • Để trao đổi với hệ thống bên ngoài — dùng định dạng văn bản và thư viện hiện đại: JSON, XML, Jackson, Gson, JAXB.
  • Để đảm bảo tương thích, hãy dùng phiên bản hóa: lưu phiên bản của đối tượng ngay trong lớp và điều chỉnh xử lý khi giải tuần tự.
  • Nếu tuần tự hóa chỉ dùng cho cache — đừng cố duy trì tương thích bằng mọi giá: cache dễ tính toán lại.
  • Không lưu dữ liệu nhạy cảm (mật khẩu, khóa) trong các đối tượng có thể tuần tự hóa — tuần tự hóa không mã hóa dữ liệu.

5. Những lỗi thường gặp khi làm việc với tuần tự hóa nhị phân

Lỗi số 1: Giải tuần tự dữ liệu từ nguồn không tin cậy. Lỗi nguy hiểm nhất — chấp nhận và giải tuần tự các đối tượng đến “từ bên ngoài” (từ mạng, từ người dùng, từ tệp bị thay thế). Đây là con đường trực tiếp dẫn đến lỗ hổng, thậm chí RCE.

Lỗi số 2: Thay đổi cấu trúc lớp ngầm mà không cập nhật serialVersionUID. Nếu không chỉ định định danh một cách tường minh, JVM sẽ sinh tự động. Bất kỳ thay đổi nào trong cấu trúc (ngay cả thứ tự trường) sẽ dẫn đến không tương thích và không thể tải đối tượng cũ.

Lỗi số 3: Cố tuần tự hóa các đối tượng có trường không thể tuần tự hóa. Nếu lớp có một trường không triển khai Serializable, và nó không transient, quá trình tuần tự sẽ kết thúc bằng ngoại lệ.

Lỗi số 4: Lưu dữ liệu tạm thời hoặc nhạy cảm trong các đối tượng có thể tuần tự hóa. Token, mật khẩu, các descriptor tài nguyên tạm thời — tất cả có thể vô tình xuất hiện trong tệp.

Lỗi số 5: Dùng tuần tự hóa nhị phân cho lưu trữ lâu dài và trao đổi giữa các phiên bản. Sau lần cập nhật lớp đầu tiên, rủi ro dữ liệu “hỏng” và vấn đề tương thích là rất cao.

Lỗi số 6: Kỳ vọng rằng các trường static và transient sẽ “khôi phục” sau khi giải tuần tự. Các trường này không được tuần tự hóa; sau khi tải, chúng sẽ có giá trị mặc định.

1
Nhiệm vụ
JAVA 25 SELF, mức độ, bài học
Đã khóa
Trường không thể tuần tự hóa cho phiên trò chơi
Trường không thể tuần tự hóa cho phiên trò chơi
1
Nhiệm vụ
JAVA 25 SELF, mức độ, bài học
Đã khóa
Mở rộng thư viện số và kiểm tra tính tương thích
Mở rộng thư viện số và kiểm tra tính tương thích
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION