Tư duy Feynman • Trực quan hình họa • Java / Spring Boot

Chương 03: Input Validation (Kiểm định dữ liệu đầu vào)

Xây dựng thành trì kiểm định dữ liệu đầu vào trong Java: Đủ 9 khái niệm gồm Untrusted Input, Allowlist, Type Validation, Length Validation, Range Validation, Safe Regex ReDoS, Tika Magic Bytes, URL Validation và Chống XXE.

#01 Untrusted Input (Nguồn dữ liệu không tin cậy)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Hãy coi toàn bộ dữ liệu từ thế giới bên ngoài (người dùng gõ, URL, cookie, webhook, thậm chí từ đối tác) như nước sông chưa qua xử lý. Dù nó trông có trong vắt đến đâu, bạn tuyệt đối không được uống trực tiếp mà phải dẫn qua nhà máy lọc nước và đun sôi (Validation & Sanitization). Mọi nguồn dữ liệu đến từ ngoài máy chủ của bạn đều PHẢI bị coi là có chứa độc tố.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Untrusted Input (Nguồn dữ liệu không tin cậy)
Untrusted Sources Body, Query, Header, Cookie Trust Boundary Cửa ngõ Controller Validation Layer Jakarta @Valid + Schema Business Logic Dữ liệu đã khử trùng 🛡️ Zero Trust Input: Thiết lập ranh giới tin cậy kiểm định 100% dữ liệu đầu vào
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: Dữ liệu Query paramsHTTP Headers (Referer, User-Agent)Cookie value bị sửa đổi
⚠️ Tin tưởng HTTP Header: Đưa thẳng User-Agent vào câu lệnh SQL hoặc ghi vào log không qua xử lý dẫn tới SQL Injection hoặc Log4j RCE!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// SAI LẦM: Nghĩ rằng Header HTTP là an toàn
@GetMapping("/stats")
public void logStats(@RequestHeader("User-Agent") String ua) {
    jdbcTemplate.execute("INSERT INTO logs (ua) VALUES ('" + ua + "')");
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN: Mọi nguồn dữ liệu đều là Untrusted Input
@GetMapping("/stats")
public void logStats(@RequestHeader("User-Agent") String ua) {
    String cleanUa = ua != null ? ua.substring(0, Math.min(ua.length(), 255)) : "";
    jdbcTemplate.update("INSERT INTO logs (ua) VALUES (?)", cleanUa);
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Nguồn dữ liệu nào sau đây được coi là 'Đáng tin cậy' (Trusted) mà không cần xác thực trước khi sử dụng?
A. KHÔNG CÓ nguồn nào kể trên. Mọi dữ liệu từ bên ngoài máy chủ gửi đến đều là Untrusted Input.
B. Dữ liệu gửi từ ứng dụng di động chính thức của công ty.
C. Dữ liệu nhận từ Webhook của bên thứ ba.
D. Các giá trị lưu trong Cookie của người dùng.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#02 Allowlist Validation (Xác thực theo danh sách cho phép)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Có 2 cách bảo vệ bữa tiệc VIP: Cách 1 (Blacklist): Dán danh sách 10 tên trộm khét tiếng ở cổng và bảo 'Cấm 10 người này vào'. Tên trộm thứ 11 hoặc kẻ đeo mặt nạ sẽ ung dung bước vào. Cách 2 (Whitelist/Allowlist): Người soát vé cầm danh sách khách mời chính xác. Ai có tên trong danh sách mới được vào, còn lại đuổi hết. Danh sách cho phép luôn chiến thắng tuyệt đối danh sách cấm.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Allowlist Validation (Xác thực theo danh sách cho phép)
Input: sort=price User gửi tham số Allowlist Set ['price', 'date', 'name'] Match Verification Nằm trong danh sách Safe ORDER BY SQL an toàn tuyệt đối 🛡️ Allowlist: Chỉ chấp nhận các giá trị được định nghĩa trước
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: sort_by=pricesort_by=created_atsort_by=id; EXEC xp_cmdshell
⚠️ Dùng Blacklist (chặn chữ 'DROP', 'SELECT'): Kẻ tấn công biến tấu thành 'DrOp' hoặc '/*comment*/DROP' để vượt qua bộ lọc!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẤT BẠI: Dùng Blacklist cố gắng lọc từ khóa SQL
if (sortBy.toUpperCase().contains("DROP") || sortBy.contains(";")) {
    throw new IllegalArgumentException();
}
jdbcTemplate.query("SELECT * FROM products ORDER BY " + sortBy, ...);
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN: Dùng Allowlist Set hoặc Enum trong Java
public enum SortField {
    PRICE("price"), DATE("created_at"), NAME("name");
    private final String column;
    SortField(String col) { this.column = col; }
    public String getColumn() { return column; }
}

@GetMapping("/products")
public List getProducts(@RequestParam(defaultValue = "DATE") SortField sort) {
    // Hoàn toàn an toàn vì Spring tự động map sang Enum hợp lệ!
    return productRepo.findAll(Sort.by(sort.getColumn()));
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Vì sao cách tiếp cận Denylist/Blacklist gần như luôn thất bại trong bảo mật phần mềm?
A. Vì kẻ tấn công luôn có thể tìm ra các biến thể mã hóa (encoding, bypass ký tự, cú pháp tương đương) mà người lập trình không lường trước được.
B. Vì danh sách đen chiếm quá nhiều RAM.
C. Vì chuẩn W3C cấm sử dụng từ blacklist.
D. Vì danh sách đen chỉ hoạt động được trên Linux.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#03 Type Validation in Java (Xác thực kiểu dữ liệu)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Nếu máy bán nước tự động chỉ chấp nhận tiền xu kim loại tròn, bạn không thể nhét một mẩu giấy hay tấm séc vào khe xu được. Java là ngôn ngữ định kiểu tĩnh (Statically Typed): Nếu hàm cần một Long ID, Spring MVC sẽ ép kiểu số; nếu client truyền chuỗi ký tự lạ, Spring ném lỗi MethodArgumentTypeMismatchException ngay lập tức.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Type Validation in Java (Xác thực kiểu dữ liệu)
GET /user/abc Tham số sai kiểu Spring Type Converter Ép kiểu sang Long Type Mismatch Ex Chặn đứng chuỗi lạ HTTP 400 Bad Request an toàn 🛡️ Type Validation: Ép chặt kiểu dữ liệu ở tầng Controller Dispatcher
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: {"age": 25}{"age": {"$gt": 0}} (NoSQL Injection){"id": "1 OR 1=1"}
⚠️ Nhận Type lỏng lẻo: Tham số truyền vào dạng chuỗi hoặc Object làm biến đổi câu truy vấn nội bộ!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// Nhận Object thô không định kiểu
@PostMapping("/find")
public void find(@RequestBody Map map) {
    // map.get("age") có thể là bất kỳ kiểu gì -> Dễ dính lỗi NoSQL/Logic!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN: Dùng Java Strong Typing với DTO định kiểu rõ ràng
public record FindUserRequest(
    @NotNull Long id,
    @Min(0) Integer age
) {}

@PostMapping("/find")
public ResponseEntity find(@Valid @RequestBody FindUserRequest req) {
    return ResponseEntity.ok(userService.findByIdAndAge(req.id(), req.age()));
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Lợi thế bảo mật tự nhiên lớn nhất của Java so với các ngôn ngữ định kiểu động (như JavaScript/PHP) trong kiểm định kiểu dữ liệu là gì?
A. Hệ thống kiểu tĩnh (Static Typing) mạnh mẽ của Java tự động từ chối các kiểu dữ liệu không khớp ngay từ khâu compile và deserialization của Spring DispatcherServlet.
B. Java chạy nhanh hơn 1000 lần.
C. Java không bao giờ bị lỗi bộ nhớ.
D. Java tự động viết code bảo mật.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#04 Length Validation (Xác thực độ dài với @Size)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Két sắt gửi thư chỉ có một khe hẹp vừa vặn với một bức thư tiêu chuẩn. Nếu kẻ xấu cố nhét một khúc gỗ dài 3 mét hay một khối thuốc nổ khổng lồ vào, khe thư sẽ chặn lại ngay. Nếu bạn không giới hạn độ dài của trường 'Họ tên', kẻ tấn công có thể gửi một chuỗi 50 Megabytes khiến máy chủ hết bộ nhớ RAM (Buffer Overflow / DoS).
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Length Validation (Xác thực độ dài với @Size)
Client Body Chuỗi 50MB @Size(max=100) Kiểm tra giới hạn ký tự Reject Oversized Từ chối chuỗi quá dài Protect Memory RAM máy chủ an toàn 🛡️ @Size / max-length: Ngăn ngừa cạn kiệt tài nguyên bộ nhớ (Memory Exhaustion)
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: Tên 10 ký tự: 'Nguyen Van'Payload DoS: 50MB chuỗi ATên 0 ký tự (Rỗng)
⚠️ Không giới hạn độ dài: Kẻ tấn công gửi payload khổng lồ làm treo tiến trình hoặc tràn bộ nhớ!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THIẾU GIỚI HẠN ĐỘ DÀI: Nhận chuỗi không hạn chế
public class CommentDto {
    private String content; // Hacker gửi chuỗi 50MB làm nghẽn RAM và CSDL!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng @Size của Jakarta Validation
public record CommentRequest(
    @NotBlank
    @Size(min = 1, max = 500, message = "Bình luận tối đa 500 ký tự")
    String content
) {}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Mục đích quan trọng nhất của việc giới hạn độ dài đầu vào (Max Length) là gì?
A. Phòng chống tấn công từ chối dịch vụ (DoS), cạn kiệt tài nguyên bộ nhớ (Memory Exhaustion) và chống tràn bộ đệm.
B. Làm cho giao diện web nhìn đẹp hơn.
C. Để bảng tính Excel xuất ra không bị lỗi cột.
D. Giúp tăng tốc độ mạng của người dùng.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#05 Range Validation (Xác thực phạm vi giá trị với @Min / @Max)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Khi bạn điền ô 'Tháng sinh', giá trị chỉ có thể hợp lệ từ số 1 đến 12. Nếu một người nhập tháng 99 hoặc số âm (-5), hệ thống phải từ chối ngay. Trong thương mại điện tử, nếu không kiểm tra phạm vi số lượng mua hàng (> 0), hacker có thể nhập số lượng mua là '-5 món đồ', khiến tổng tiền thành số âm và tài khoản của họ được cộng thêm tiền!
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Range Validation (Xác thực phạm vi giá trị với @Min / @Max)
Quantity: -5 Nhập số âm gian lận @Min(1) @Max(50) Kiểm tra phạm vi Reject Out-of-Range Chặn đứng số âm Cart Total Tính tiền chuẩn xác 🛡️ @Min / @Max Range Validation: Triệt tiêu lỗ hổng đảo chiều giao dịch
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: quantity: -5 (Số âm để rút tiền)quantity: 2 (Hợp lệ)quantity: 999999999 (Tràn số Integer)
⚠️ Lỗi Logic Nghiêm Trọng: Đơn hàng chấp nhận số lượng -1 chiếc bánh giá 100$, hệ thống tính tổng tiền = -100$ và hoàn tiền vào ví hacker!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẢM HỌA: Không kiểm tra phạm vi số lượng
@PostMapping("/checkout")
public ResponseEntity checkout(@RequestBody OrderItemDto item) {
    // Nếu item.getQuantity() = -5 -> total = -500$ (Hoàn tiền cho hacker!)
    BigDecimal total = item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()));
    paymentService.charge(total);
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng @Min và @Max
public record OrderItemRequest(
    @NotNull Long productId,
    @Min(value = 1, message = "Số lượng tối thiểu là 1")
    @Max(value = 50, message = "Số lượng tối đa là 50 sản phẩm mỗi đơn")
    int quantity
) {}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Hậu quả kinh điển nhất của việc thiếu kiểm tra phạm vi giá trị (Range Validation) trong tính năng mua hàng là gì?
A. Kẻ tấn công truyền số lượng âm để đảo ngược giao dịch, biến lệnh thanh toán thành lệnh hoàn tiền vào ví cá nhân.
B. Người dùng không nhận được email xác nhận.
C. Giao diện website chuyển sang màu đen.
D. Trình duyệt bị treo tab.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#06 Format Validation & Safe Regex (Chống ReDoS trong Java)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Định dạng giống như chiếc khuôn đúc bánh. Bánh quy hình ngôi sao chỉ lọt qua chiếc khuôn ngôi sao. Tuy nhiên, nếu chiếc khuôn được chế tạo bằng vật liệu lỏng lẻo có quá nhiều răng cưa lặp đi lặp lại (Biểu thức chính quy Regex không an toàn), khi gặp một mẩu bột dài đặc biệt, cỗ máy sẽ bị kẹt bánh răng và quay cuồng mãi mãi cho đến khi bốc khói (ReDoS - Regular Expression Denial of Service).
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Format Validation & Safe Regex (Chống ReDoS trong Java)
Evil Input aaaaa...! Vulnerable Regex (a+)+$ Backtracking ReDoS Attack CPU 100% treo JVM Safe Regex / Timeout Khớp tuyến tính an toàn 🛡️ Chống ReDoS: Tránh các nhóm lặp lồng nhau và thiết lập Regex Timeout
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: user@example.comnot-an-emailaaaaaaaaaaaaaaaaaaaaaaaaaaaa! (ReDoS payload)
⚠️ Regex kiểm tra email bị lỗi Catastrophic Backtracking: Chuỗi 'aaaaa!' khiến CPU nhảy lên 100%!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// CỰC KỲ NGUY HIỂM: Regex lồng lặp (a+)+
Pattern pattern = Pattern.compile("^([a-zA-Z0-9]+)+$");
// Chuỗi "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" làm treo luồng Java vĩnh viễn!
Matcher matcher = pattern.matcher(userInput);
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng thư viện Commons Validator hoặc Regex tuyến tính
import org.apache.commons.validator.routines.EmailValidator;

boolean isValidEmail = EmailValidator.getInstance().isValid(email);

// Hoặc thiết lập Timeout cho CharSequence khi chạy Regex:
// Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Hiện tượng 'Catastrophic Backtracking' trong ReDoS xảy ra khi nào?
A. Khi Regex chứa các nhóm lặp lồng nhau (Nested Quantifiers như (a+)+) khiến số bước quay lui tăng theo cấp số nhân khi chuỗi không khớp ở ký tự cuối.
B. Khi máy chủ bị ngắt kết nối internet.
C. Khi file code có dung lượng lớn hơn 10MB.
D. Khi sử dụng hàm String.replace().
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#07 File Input Validation with Apache Tika (Xác thực tập tin)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Một tên gián điệp mặc áo blouse trắng và đeo thẻ Bác sĩ giả để lọt vào phòng thí nghiệm bí mật. Nếu bảo vệ chỉ nhìn chiếc áo blouse (đuôi file .jpg), hắn sẽ lọt qua dễ dàng. Người bảo vệ chuẩn mực phải bắt hắn mở balo ra xem bên trong có dao kéo súng đạn hay không (dùng thư viện Apache Tika soi chữ ký Magic Bytes ở đầu file).
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): File Input Validation with Apache Tika (Xác thực tập tin)
Uploaded File avatar.jpg Apache Tika Soi Magic Bytes MIME Check Xác thực image/jpeg Isolated Storage Lưu trữ an toàn 🛡️ Xác thực tệp tin đa tầng: Extension + MIME + Magic Bytes với Apache Tika
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: shell.php.jpgavatar.pngmalware.exe đổi đuôi thành .pdf
⚠️ Chỉ kiểm tra đuôi file bằng String.endsWith('.jpg'): Hacker tải lên mã độc shell.php có đuôi trá hình và thực thi chiếm quyền máy chủ!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẢM HỌA: Tin vào file.getContentType() do client gửi
if ("image/jpeg".equals(file.getContentType())) {
    file.transferTo(new File("/uploads/" + file.getOriginalFilename()));
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng Apache Tika đọc Magic Bytes
Tika tika = new Tika();
String actualMime = tika.detect(file.getInputStream());
if (!List.of("image/jpeg", "image/png").contains(actualMime)) {
    throw new IllegalArgumentException("File không phải là ảnh hợp lệ!");
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Thuật ngữ 'Magic Bytes' (File Signature) trong xác thực tập tin có ý nghĩa gì?
A. Các byte đầu tiên bất biến nằm ở đầu tệp tin giúp xác định chính xác định dạng thực sự của tệp bất kể phần mở rộng bị đổi tên.
B. Một câu thần chú trong Java.
C. Kích thước tối đa của tệp tin.
D. Chữ ký mã hóa của Google Chrome.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#08 URL Validation in Java (Xác thực đường dẫn URL & Chống SSRF)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn đưa địa chỉ nhờ tài xế giao hàng: 'Hãy đến số 10 đường Hoa Mai lấy tài liệu'. Nếu bạn không kiểm tra địa chỉ mà người khác lén ghi vào tờ giấy: 'Hãy đến kho vũ khí quân sự tuyệt mật trong căn cứ nội bộ (127.0.0.1 hay 169.254.169.254)', tài xế ngây thơ sẽ lái xe đến đó và mang tài liệu mật nội bộ về cho kẻ xấu (SSRF - Server-Side Request Forgery).
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): URL Validation in Java (Xác thực đường dẫn URL & Chống SSRF)
Target URL http://169.254.169.254 Scheme & Domain Check Chỉ https:// + Allowlist DNS Resolve InetAddress chặn Private IP Protected Network Không rò rỉ Metadata 🛡️ URL Validation: Bắt buộc HTTPS, kiểm tra Domain Whitelist và chặn IP nội bộ
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: http://169.254.169.254/latest/meta-data/http://localhost:8080/adminhttps://api.partner.com/webhook
⚠️ URL nhắm vào AWS Metadata Service: Server thực hiện request tới 169.254.169.254 và rò rỉ toàn bộ IAM Credentials của Cloud!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// NGUY HIỂM: Tải URL người dùng truyền vào mà không kiểm tra
String content = restTemplate.getForObject(userSuppliedUrl, String.class);
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Kiểm tra Scheme và phân giải IP chống SSRF
URI uri = new URI(userSuppliedUrl);
if (!"https".equalsIgnoreCase(uri.getScheme())) throw new SecurityException("Chỉ chấp nhận HTTPS");

InetAddress[] addrs = InetAddress.getAllByName(uri.getHost());
for (InetAddress addr : addrs) {
    if (addr.isLoopbackAddress() || addr.isSiteLocalAddress()) {
        throw new SecurityException("Cấm truy cập mạng nội bộ!");
    }
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Địa chỉ IP 169.254.169.254 đóng vai trò gì trong các cuộc tấn công SSRF trên môi trường Cloud?
A. Là địa chỉ của Instance Metadata Service (IMDS) dùng để trích xuất IAM Roles, Token và thông tin mật của máy chủ ảo.
B. Là IP của máy chủ DNS quốc tế.
C. Là cổng thanh toán của ngân hàng.
D. Là địa chỉ loopback của trình duyệt.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#09 XML & JSON Schema Validation in Java (Chống XXE & Injection)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Một hợp đồng pháp lý cần có các điều khoản mẫu rõ ràng. Nếu bạn cho phép đối tác tự ý viết thêm một điều khoản: 'Người mang hợp đồng này được quyền lấy luôn tòa nhà quốc hội' (Khai báo thực thể ngoài XML Entity - XXE) mà bạn vẫn ký đóng dấu, tòa nhà sẽ bị tịch thu. Trong Java, việc cấu hình DocumentBuilderFactory tắt hoàn toàn DTD và External Entities là bắt buộc để triệt tiêu XXE.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): XML & JSON Schema Validation in Java (Chống XXE & Injection)
XML Input ]> DocumentBuilderFactory setFeature(DISALLOW_DOCTYPE, true) XXE Blocked Ném ParserConfigurationException Safe DOM Tree Không đọc trộm file hệ thống 🛡️ Chống XXE trong Java: Vô hiệu hóa tính năng DTD và External Entities
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: ]>JSON Schema chuẩnMass Assignment Payload
⚠️ Lỗ hổng XML External Entity (XXE): Bộ phân tích XML đọc nội dung file hệ thống /etc/passwd và trả về cho hacker!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// THẢM HỌA: Dùng DocumentBuilderFactory mặc định của Java
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder(); // Dính XXE 100%!
Document doc = db.parse(new InputSource(new StringReader(xmlString)));
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN BẢO MẬT OWASP CHO JAVA XML PARSER:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Vô hiệu hóa DTD và External Entities hoàn toàn:
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Biện pháp hiệu quả nhất để phòng chống triệt để lỗ hổng XXE (XML External Entity) trong Java là gì?
A. Cấu hình DocumentBuilderFactory với thuộc tính 'disallow-doctype-decl' = true để cấm hoàn toàn khai báo DOCTYPE.
B. Mã hóa file XML bằng Base64.
C. Chỉ dùng file XML dưới 1KB.
D. Đổi đuôi file từ .xml sang .json.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)