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

Chương 10: Path Traversal Mitigation

Triệt tiêu lỗ hổng duyệt thư mục lùi: Chuẩn hóa Path.normalize(), kiểm định Canonical Path và cô lập ranh giới lưu trữ tệp tin.

#01 Directory Traversal / Dot-Dot-Slash (Duyệt thư mục lùi)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn đến quầy dịch vụ khách hàng của tòa nhà và bảo: 'Lấy cho tôi tài liệu trong ngăn kéo số 5'. Nhưng bạn lách luật nói: 'Lấy cho tôi tài liệu trong: Ngăn kéo số 5 / Bước lùi ra cửa (..) / Bước lùi lên tầng 2 (..) / Bước vào phòng Giám Đốc (..) / Mở két sắt lấy file mật'. Ký tự '../' chính là những bước lùi giúp kẻ xấu thoát ra khỏi ranh giới thư mục được phép để lục lọi toàn bộ ổ cứng máy chủ.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Directory Traversal / Dot-Dot-Slash (Duyệt thư mục lùi)
Client Path input: ../../etc/passwd Path Sanitizer normalize() & getCanonicalFile() Boundary Check startsWith(safeDirectory) Result Chặn đứng hoặc Đọc an toàn ⚠️ Chống Path Traversal: Directory Traversal / Dot-Dot-Slash (Duy
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: ../../../../etc/passwd..\\..\\..\\windows\\win.ini%2e%2e%2f%2e%2e%2f (URL Encoded)
⚠️ Lỗ hổng Directory Traversal: Kẻ tấn công dùng chuỗi '../' nhảy ngược về thư mục gốc hệ thống và đọc toàn bộ file /etc/passwd!
⚖️ 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: Ghép trực tiếp tên file từ request vào đường dẫn thư mục
@GetMapping("/download")
public ResponseEntity download(@RequestParam String filename) {
    File file = new File("/var/data/files/" + filename); // filename: "../../etc/passwd"
    return ResponseEntity.ok(new FileSystemResource(file));
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Kiểm tra và chặn các ký tự lùi thư mục
Path basePath = Paths.get("/var/data/files").toAbsolutePath().normalize();
Path targetPath = basePath.resolve(filename).normalize();

if (!targetPath.startsWith(basePath)) {
    throw new SecurityException("Phát hiện hành vi Path Traversal bất hợp pháp!");
}
return ResponseEntity.ok(new UrlResource(targetPath.toUri()));
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Hàm path.basename('../../etc/passwd') trong Node.js sẽ trả về kết quả là gì?
A. 'passwd' (Tước bỏ hoàn toàn phần điều hướng thư mục phía trước)
B. '../../etc/passwd'
C. null
D. Ném ra lỗi Exception
📝 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 Path Normalization (Chuẩn hóa đường dẫn)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Trước khi kiểm tra xem một con đường có an toàn hay không, bạn phải 'dọn dẹp' các biển chỉ dẫn lòng vòng: Con đường 'A / đi tới B / quay lui về A (..) / rẽ vào C' thực chất chính là con đường 'A / C'. Chuẩn hóa đường dẫn là bước rút gọn mọi đường dẫn zíc zắc về dạng trực tiếp ngắn gọn nhất trước khi đem ra đối chiếu an ninh.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Path Normalization (Chuẩn hóa đường dẫn)
Client Path input: ../../etc/passwd Path Sanitizer normalize() & getCanonicalFile() Boundary Check startsWith(safeDirectory) Result Chặn đứng hoặc Đọc an toàn ⚠️ Chống Path Traversal: Path Normalization (Chuẩn hóa đường dẫn)
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: /public/images/../../secretsa/b/c/../../dfile:///app/static/../.env
⚠️ Chưa chuẩn hóa: Đường dẫn chứa các bước nhảy lòng vòng chưa được giải quyết khiến việc kiểm tra chuỗi đơn thuần bị đánh lừa!
⚖️ 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: Chỉ replace chuỗi "../" một lần duy nhất
String sanitized = path.replace("../", ""); // Bypass dễ dàng bằng "....//" hoặc "..%2f" 
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Dùng java.nio.file.Path.normalize()
Path cleanPath = Paths.get(input).normalize();
// Loại bỏ hoàn toàn các ký tự dư thừa "." và ".." theo chuẩn POSIX
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Vì sao kỹ thuật lọc chuỗi đơn thuần 'input.replace('../', '')' lại rất dễ bị kẻ tấn công vượt qua?
A. Vì kẻ tấn công chỉ cần gửi chuỗi '....//' - khi hàm replace xóa '../' ở giữa thì các ký tự còn lại ghép lại tạo thành chuỗi '../' mới nguyên vẹn.
B. Vì hàm replace không chạy được trên Windows.
C. Vì dấu chấm không phải là ký tự hợp lệ.
D. Vì trình duyệt sẽ tự động thêm dấu phẩy.
📝 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 Canonical Path Resolution (Giải quyết đường dẫn chuẩn tắc)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn có một danh sách địa chỉ cấm. Nhưng một ngôi nhà có thể có nhiều tên gọi khác nhau: 'Số 10 phố Hoa', 'Căn nhà cạnh tiệm bánh mì', hoặc qua một lối đi tắt bí mật (Symlink / Liên kết mềm). Canonical Path giống như Tọa độ vệ tinh GPS duy nhất của ngôi nhà. Bất kể bạn gọi nó bằng tên lóng hay lối đi tắt nào, máy tính đều quy về đúng tọa độ thực tế trên bề mặt trái đất để so sánh.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Canonical Path Resolution (Giải quyết đường dẫn chuẩn tắc)
Client Path input: ../../etc/passwd Path Sanitizer normalize() & getCanonicalFile() Boundary Check startsWith(safeDirectory) Result Chặn đứng hoặc Đọc an toàn ⚠️ Chống Path Traversal: Canonical Path Resolution (Giải quyết đư
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: Khai thác Symbolic Link (Symlink)fs.realpathSync() xác thựcURL Encoded path traversal
⚠️ Lỗ hổng Symlink Traversal: Kẻ tấn công tạo liên kết mềm trỏ ra thư mục gốc để vượt mặt kiểm tra đường dẫn thông thường!
⚖️ 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: Không giải quyết liên kết mềm (Symlink)
File f = new File(baseDir, filename);
if (f.getAbsolutePath().startsWith(baseDir.getAbsolutePath())) {
    // Nếu filename là một Symlink trỏ đến /etc/shadow -> Vẫn bị vượt rào!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Dùng toRealPath() hoặc getCanonicalFile()
Path baseDir = Paths.get("/var/app/data").toRealPath();
Path resolved = baseDir.resolve(filename).toRealPath();

if (!resolved.startsWith(baseDir)) {
    throw new SecurityException("Symlink hoặc đường dẫn vượt ra ngoài ranh giới cho phép!");
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Phương thức nào trong Node.js giải quyết triệt để các liên kết mềm (Symbolic Links) để lấy về đường dẫn chuẩn tắc (Canonical Path) thực sự trên ổ cứng?
A. fs.realpathSync()
B. path.extname()
C. os.hostname()
D. process.cwd()
📝 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 File Path Validation (Chiến lược kiểm tra đường dẫn an toàn)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Thay vì đoán xem khách hàng có đang giấu chìa khóa vạn năng để mở các phòng khác hay không, bạn đưa cho họ một bảng danh mục cố định gồm 5 cuốn sách: [1, 2, 3, 4, 5]. Khách chỉ được chọn số 1 hoặc số 2. Hệ thống tự động ánh xạ con số đó với đường dẫn tệp an toàn bên trong máy chủ. Người dùng hoàn toàn không có cơ hội chạm vào bất kỳ chuỗi đường dẫn tệp nào.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): File Path Validation (Chiến lược kiểm tra đường dẫn an toàn)
Client Path input: ../../etc/passwd Path Sanitizer normalize() & getCanonicalFile() Boundary Check startsWith(safeDirectory) Result Chặn đứng hoặc Đọc an toàn ⚠️ Chống Path Traversal: File Path Validation (Chiến lược kiểm tr
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: Ánh xạ qua ID số nguyên: 4Allowlist tên tệp: ['terms.pdf', 'policy.pdf']Truyền chuỗi tự do đường dẫn tệp
⚠️ Cho phép truyền đường dẫn chuỗi tự do: Mở toang nguy cơ bị tấn công Path Traversal nếu thiếu sót bất kỳ bước kiểm tra nào!
⚖️ 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: Chấp nhận mọi ký tự trong tên file
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Danh sách trắng ký tự tên file an toàn
private static final Pattern SAFE_FILENAME = Pattern.compile("^[a-zA-Z0-9._-]{1,255}$");

public static String validateFilename(String name) {
    if (!SAFE_FILENAME.matcher(name).matches() || name.contains("..")) {
        throw new IllegalArgumentException("Tên tệp không hợp lệ");
    }
    return name;
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Chiến lược nào được xem là an toàn và thanh lịch nhất để triệt tiêu hoàn toàn nguy cơ Path Traversal trong việc tải tài liệu?
A. Sử dụng mã định danh gián tiếp (Indirect Object Reference / Allowlist Mapping) - người dùng chỉ truyền ID vô hại, server tự ánh xạ sang đường dẫn thực tế cố định.
B. Chỉ cho phép tải file vào ban ngày.
C. Đổi màu nền của trang web.
D. Mã hóa tên file bằng Base64 trên thanh URL.
📝 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)