Nguyên Tắc Mã Hóa An Toàn Cho Ứng Dụng Java

1. Xác Thực Dữ Liệu Đầu Vào

Nguyên tắc cốt lõi: Tách biệt hoàn toàn giữa dữ liệu và mã lệnh, đặc biệt chú ý đến các ký tự đặc biệt của ngôn ngữ.

1.1 Phòng Chống Tấn Công SQL Injection

Mức độ rủi ro: Tác động cao, khả năng xảy ra trung bình.

Biện pháp xử lý:

(1) Kiểm soát tham số đầu vào bằng danh sách trắng (whitelist):

public boolean isValidUsername(String input) {
    if (input == null) return false;
    return input.matches("^[a-zA-Z0-9_]{3,20}$");
}

(2) Sử dụng câu lệnh chuẩn bị sẵn (PreparedStatement):

String query = "SELECT account_balance FROM users WHERE user_id = ? AND status = ?";
try (PreparedStatement stmt = dbConnection.prepareStatement(query)) {
    stmt.setInt(1, userId);
    stmt.setString(2, "ACTIVE");
    ResultSet rs = stmt.executeQuery();
    // xử lý kết quả
}

1.2 Phòng Chống Tấn Công XSS

Mức độ rủi ro: Tác động trung bình, khả năng xảy ra cao.

Các phương án bảo vệ:

(1) Kiểm tra chặt chẽ dữ liệu đầu vào và đầu ra (ưu tiên whitelist);

(2) Sử dụng các thư viện tiện ích như org.apache.commons.text để thoát ký tự;

(3) Đối với nội dung rich-text, áp dụng các bộ lọc như OWASP AntiSamy;

(4) Mã hóa dữ liệu theo ngữ cảnh sử dụng (HTML, JS, CSS, URL):

// Mã hóa cho ngữ cảnh HTML
String safeHtml = ESAPI.encoder().encodeForHTML(userInput);
// Mã hóa cho thuộc tính HTML
String safeAttr = ESAPI.encoder().encodeForHTMLAttribute(userInput);
// Mã hóa cho JavaScript
String safeJs = ESAPI.encoder().encodeForJavaScript(userInput);

1.3 Phòng Chống Inject Mã Lệnh

Mức độ rủi ro: Tác động cao.

(1) Validate tham số đầu vào;

(2) Tránh thực thi trực tiếp lệnh từ người dùng. Nếu bắt buộc, sử dụng danh sách lệnh cho phép:

String action = request.getParameter("action");
if ("status".equals(action)) {
    // Chỉ cho phép chạy lệnh kiểm tra trạng thái cố định
    Runtime.getRuntime().exec("/usr/bin/check_status.sh");
}

(3) Cập nhật thường xuyên các thư viện bên thứ ba như Spring, Struts.

1.4 Phòng Chống Giả Mạo Log

Mức độ rủi ro: Khả năng xảy ra cao.

(1) Hạn chế ghi log dữ liệu do người dùng kiểm soát;

(2) Loại bỏ các ký tự xuống dòng khỏi dữ liệu đầu vào trước khi ghi:

String cleanData = data.replaceAll("(?i)(%0a|%0d|\\n|\\r)", "");
logger.info("User input: " + cleanData);

(3) Sử dụng các framework logging hiện đại như Log4j2 với cấu hình bảo mật.

1.5 Phòng Chống XXE (XML External Entity)

Mức độ rủi ro: Trung bình.

(1) Vô hiệu hóa việc phân tích DTD và实体 bên ngoài:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

(2) Lọc các từ khóa nguy hiểm như DOCTYPE, ENTITY trong dữ liệu XML nhận được.

1.6 Phòng Chống Inject XML

(1) Kiểm tra dữ liệu đầu vào;

(2) Sử dụng các thư viện XML an toàn đã được kiểm chứng.

1.7 Phòng Chống Chuyển Hướng URL

(1) Xác thực URL đích dựa trên danh sách trắng các domain tin cậy:

String targetUrl = request.getParameter("redirect");
if (!allowedDomains.contains(getHost(targetUrl))) {
    response.sendError(400, "Invalid redirect URL");
    return;
}

(2) Sử dụng token xác thực cho mỗi lần chuyển hướng;

(3) Kiểm tra header Referer để hỗ trợ giám sát.

2. Xử Lý Ngoại Lệ

Nguyên tắc: Không để lộ thông tin nhạy cảm qua thông báo lỗi.

2.1 Ngăn Chặn Rò Rỉ Thông Tin

Tránh in stack trace hoặc mã lỗi chi tiết ra giao diện người dùng:

try {
    // xử lý logic
} catch (Exception e) {
    logger.error("System error occurred", e);
    response.sendError(500, "Có lỗi xảy ra, vui lòng thử lại");
}

2.2 Duy Trì Tính Nhất Quán Của Đối Tượng

(1) Thực hiện các kiểm tra trước khi thay đổi trạng thái đối tượng;

(2) Sử dụng cơ chế transaction để rollback khi có lỗi;

(3) Làm việc trên bản sao tạm trước khi commit vào đối tượng chính;

(4) Thiết kế đối tượng bất biến (immutable) khi có thể.

3. Thao Tác I/O

Nguyên tắc: Tệp thực thi không được ghi, tệp ghi không được thực thi.

3.1 Giải Phóng Tài Nguyên

Các tài nguyên không phải bộ nhớ (Connection, Stream, Socket) cần được đóng thủ công. Sử dụng try-with-resources:

try (Connection conn = dataSource.getConnection();
     PreparedStatement stmt = conn.prepareStatement(sql)) {
    // thực thi truy vấn
} catch (SQLException e) {
    // xử lý lỗi
}

3.2 Xóa Tệp Tạm

(1) Tự động xóa khi JVM thoát hoặc đóng stream:

Path tempPath = Files.createTempFile("prefix", ".tmp");
try (BufferedWriter writer = Files.newBufferedWriter(tempPath, StandardOpenOption.DELETE_ON_CLOSE)) {
    writer.write("data");
}

(2) Xóa thủ công sau khi sử dụng xong.

3.3 Bảo Vệ Buffer

Không trả về buffer gốc có thể ghi cho code không tin cậy. Sử dụng chế độ chỉ đọc:

private CharBuffer internalBuffer = CharBuffer.allocate(1024);

public CharBuffer getReadOnlyView() {
    return internalBuffer.asReadOnlyBuffer();
}

3.4 Phòng Chống Path Traversal

(1) Validate tham số đường dẫn;

(2) Ánh xạ ID tệp thay vì truyền đường dẫn trực tiếp:

String fileId = request.getParameter("fid");
String realPath = fileService.getSecurePath(fileId);
downloadFile(realPath);

(3) Kiểm tra thư mục gốc và phần mở rộng tệp;

(4) Không lưu trữ file cấu hình hoặc log trong thư mục public web.

3.5 Phòng Chống Upload Tệp Độc Hại

Sử dụng whitelist cho phần mở rộng và đổi tên tệp ngẫu nhiên:

if (!allowedExtensions.contains(fileExtension)) {
    throw new SecurityException("Invalid file type");
}
String storedName = UUID.randomUUID().toString() + "." + fileExtension;

4. Serialization và Deserialization

Nguyên tắc: Không tin tưởng dữ liệu serialized từ bên ngoài.

4.1 Bảo Vệ Dữ Liệu Nhạy Cảm

Sử dụng từ khóa transient hoặc tùy chỉnh quá trình serialization:

private transient String passwordHash;
private static final ObjectStreamField[] serialPersistentFields = {
    new ObjectStreamField("userId", Long.TYPE)
};

4.2 Kiểm Tra Bảo Mật Khi Deserialization

Thực hiện lại các kiểm tra bảo mật trong phương thức readObject:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(new SerializablePermission("enableSubstitution"));
    in.defaultReadObject();
}

5. Môi Trường Thực Thi

Nguyên tắc: Giảm thiểu bề mặt tấn công.

5.1 Bật Xác Thực Bytecode

Đảm bảo JVM chạy với tùy chọn xác thực: -Xverify:all.

5.2 Vô Hiệu Hóa Debug Từ Xa

(1) Không sử dụng các flag như -Xdebug, -Xrunjdwp trên môi trường production;

(2) Chặn cổng debug qua firewall hoặc iptables;

(3) Kích hoạt Security Manager khi khởi chạy ứng dụng:

java -Djava.security.manager -Djava.security.policy==policy.file AppMain

5.3 Điểm Entry Duy Nhất

Loại bỏ các phương thức main thừa trong các class utility hoặc library.

6. Logic Nghiệp Vụ

Nguyên tắc: Thiết kế API an toàn ngay từ đầu.

6.1 Hệ Thống Người Dùng

Quy trình bảo mật bao gồm: Định danh → Xác thực → Phân quyền → Truy cập → Ghi log.

(1) Xác thực:

  • Áp dụng xác thực đa yếu tố (MFA);
  • Chống brute-force: CAPTCHA, khóa tài khoản tạm thời;
  • Mã hóa dữ liệu nhạy cảm khi lưu và truyền;
  • Thực hiện validation quan trọng phía server.

(2) Phân quyền:

  • Lấy thông tin user từ Session server-side;
  • Vô hiệu hóa tài khoản mặc định;
  • Phân tách nhiệm vụ (Separation of Duties);
  • Mã hóa dữ liệu truyền tải.

(3) Quản lý Session:

Lỗ HổngBiện Pháp Phòng Ngừa
Session ID trong URLLưu Session ID trong Cookie
Thiếu kiểm tra SessionYêu cầu Session cho mọi request nhạy cảm
Session không được hủyXóa Session khi logout, timeout hoặc đóng browser
Session FixationRegenerate Session ID sau khi login
Session ID dễ đoánSử dụng cơ chế tạo ID ngẫu nhiên mạnh
Replay AttackSử dụng nonce hoặc timestamp giới hạn

Lưu ý: Cookie xác thực cần thuộc tính SecureHttpOnly.

(4) Ghi log nghiệp vụ:

  • Ghi lại các truy cập dữ liệu nhạy cảm;
  • Bảo vệ file log khỏi bị giả mạo;
  • Giám sát mọi hành động đọc/ghi log.

6.2 Thanh Toán Trực Tuyến

(1) Ký số dữ liệu giao dịch và kiểm tra tính toàn vẹn;

(2) Validate các tham số giá tiền, số lượng;

(3) Đảm bảo mỗi đơn hàng là duy nhất để chống replay.

6.3 Thực Thi Tuần Tự

(1) Mỗi bước trong quy trình phải được xác thực dựa trên kết quả bước trước;

(2) Sử dụng token ngẫu nhiên xuyên suốt quy trình để đảm bảo tính liên tục và bảo mật.

Thẻ: Java secure-coding owasp input-validation Serialization

Đăng vào ngày 1 tháng 6 lúc 22:20