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

Chương 05: Cross-Site Scripting — XSS (Tấn công kịch bản chéo trang)

Hóa giải XSS trong hệ sinh thái Java & Spring Boot: Đủ 6 khái niệm gồm Reflected XSS, Stored XSS, DOM-based XSS, Context-aware Output Encoding (OWASP Java Encoder), HTML Escaping 5 ký tự cốt lõi và Content Security Policy (CSP).

#01 Reflected XSS in Java Web Applications

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn soi gương vào một hồ nước trong vắt. Kẻ xấu ném một viên thuốc màu độc hại vào mặt nước (gửi link chứa mã độc cho bạn). Khi bạn nhìn vào hình ảnh phản chiếu của mình trong hồ, chất độc bắn thẳng vào mắt bạn. Reflected XSS không lưu trên máy chủ; nó đi từ link lừa đảo, phản chiếu qua máy chủ và thực thi ngay lập tức trên trình duyệt của chính nạn nhân bấm vào link.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Reflected XSS in Java Web Applications
Phishing Link /search?q= Spring Controller Phản chiếu input HtmlUtils.htmlEscape Mã hóa thực thể <script> Safe Render Chỉ hiển thị chữ thô 🛡️ Reflected XSS: Mã hóa đầu ra Context-aware Encoding ngăn ngừa thực thi mã độc
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: Chào bạn: John Doe
⚠️ Lỗ hổng Reflected XSS: Đoạn script độc hại trong URL parameter 'q' được server in thẳng vào trang HTML -> Trình duyệt nạn nhân tự động thực thi mã script!
⚖️ 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: In thẳng input người dùng từ URL vào HTML phản hồi trong Java Servlet
response.setContentType("text/html");
String query = request.getParameter("q");
response.getWriter().println("

Kết quả tìm kiếm cho: " + query + "

");
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN SPRING FRAMEWORK: Dùng HtmlUtils.htmlEscape()
import org.springframework.web.util.HtmlUtils;

String safeQuery = HtmlUtils.htmlEscape(request.getParameter("q"));
response.getWriter().println("

Kết quả tìm kiếm cho: " + safeQuery + "

"); // Hoặc trả về qua Thymeleaf:

🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Kịch bản tấn công nào là đặc trưng nhất của Reflected XSS?
A. Hacker lừa nạn nhân bấm vào một liên kết (phishing link) độc hại được chế tác sẵn có chứa mã độc trong tham số URL.
B. Hacker đăng bài viết chứa virus vào diễn đàn công khai.
C. Hacker đột nhập trực tiếp vào phòng máy chủ để cắm USB.
D. Hacker nghe lén đường cáp quang biển.
📝 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 Stored XSS Prevention (XSS lưu trữ / Persistent XSS)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Kẻ gian lén tiêm thuốc độc vào giếng nước công cộng của làng và bỏ đi. Bất kỳ người dân nào trong làng ra múc nước uống sau đó đều bị trúng độc mà không hề hay biết. Stored XSS là dạng XSS nguy hiểm nhất: Mã độc được lưu vĩnh viễn trong Database (bình luận, tên hồ sơ, bài viết). Mỗi khi có người dùng khác truy cập vào trang đó, mã độc tự động chạy và lây nhiễm hàng loạt.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Stored XSS Prevention (XSS lưu trữ / Persistent XSS)
Attacker Comment Database Storage Lưu vĩnh viễn vào CSDL OWASP HTML Sanitizer Khử trùng thẻ độc hại Thymeleaf th:text Hiển thị an toàn cho mọi user 🛡️ Stored XSS: Khử trùng nội dung bằng OWASP Java HTML Sanitizer trước khi render
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: `)" title="Nạp payload">Bình luận an toàn
⚠️ Lỗ hổng Stored XSS: Đoạn script độc hại được lưu vào Database trong bảng Comments. Mọi khách truy cập vào đọc bài viết này đều bị đánh cắp Cookie tài khoản!
⚖️ 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 th:utext (Unescaped Text) trong Thymeleaf
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng OWASP Java HTML Sanitizer cho Rich Text
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;

public String sanitizeHtml(String rawUserInput) {
    // Chỉ cho phép định dạng chữ cơ bản và link an toàn, gọt bỏ script & onerror
    PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
    return policy.sanitize(rawUserInput);
}

// Và luôn ưu tiên th:text trên giao diện:
// 
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Vì sao Stored XSS thường có mức độ rủi ro nghiêm trọng hơn Reflected XSS?
A. Vì mã độc được lưu vĩnh viễn trên cơ sở dữ liệu của nạn nhân, tự động tấn công bất kỳ ai truy cập mà không cần phải gửi liên kết lừa đảo cho từng người.
B. Vì Stored XSS chỉ tấn công được trên trình duyệt Firefox.
C. Vì Stored XSS làm hỏng ổ cứng máy chủ.
D. Vì Stored XSS không thể ngăn chặn bằng lập trình.
📝 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 DOM-based XSS (XSS trên mô hình Client DOM)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Một vị khách đưa cho bạn một bức thư có đoạn hướng dẫn: 'Hãy tự lấy dao tự đâm vào tay mình'. Máy chủ Java thậm chí không biết gì về bức thư này. Nhưng đoạn mã JavaScript chạy trên trình duyệt của bạn đọc bức thư đó từ thanh địa chỉ (location.hash) và ngoan ngoãn làm theo hướng dẫn độc hại bằng cách gọi hàm element.innerHTML. Lỗ hổng hoàn toàn diễn ra ở Client-side.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): DOM-based XSS (XSS trên mô hình Client DOM)
Source: location.hash # Client JavaScript Đọc dữ liệu từ URL Dangerous Sink element.innerHTML = hash Safe Sink (.textContent) Coi mọi thứ là Text thuần 🛡️ DOM XSS: Thay thế Sink nguy hiểm innerHTML bằng Safe Sink textContent
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: #default_tab=profile#javascript:alert(1)
⚠️ Lỗ hổng DOM XSS: Mã JS đọc trực tiếp từ window.location.hash (Source) và gán vào innerHTML (Sink)!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// LỖ HỔNG DOM XSS TRÊN TRÌNH DUYỆT (CLIENT JAVASCRIPT)
const hash = window.location.hash.substring(1);
// Sink nguy hiểm: innerHTML thực thi script
document.getElementById("greeting").innerHTML = "Xin chào " + hash;
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Dùng Safe Sink (.textContent)
const hash = window.location.hash.substring(1);
// textContent tuyệt đối an toàn, coi mọi thứ là Text thuần túy
document.getElementById("greeting").textContent = "Xin chào " + hash;
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Hàm/Thuộc tính DOM nào sau đây được coi là 'Safe Sink' (An toàn, không bị DOM XSS) khi hiển thị văn bản người dùng?
A. element.textContent
B. element.innerHTML
C. document.write()
D. eval()
📝 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 Context-aware Output Encoding in Java

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Nếu bạn muốn mang một con hổ qua sân bay, bạn không thể để nó đi bộ tự do bên cạnh bạn. Bạn phải nhốt nó vào chiếc lồng sắt chuyên dụng của sở thú. Output Encoding chính là 'chiếc lồng sắt': Nó biến đổi những ký tự nguy hiểm thành các thực thể vô hại trước khi in ra màn hình. Tùy thuộc vào con hổ đang ở trong cabin máy bay (HTML body), trong khoang hành lý (JS variable) hay trên đường băng (URL), chiếc lồng phải có thiết kế tương ứng phù hợp với từng ngữ cảnh.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): Context-aware Output Encoding in Java
User Input alert(1) Context Detection HTML vs Attribute vs JS vs URL Encoder Mapping OWASP Java Encoder Safe Rendered Output Miễn nhiễm XSS mọi ngữ cảnh 🛡️ OWASP Java Encoder: Mã hóa theo từng ngữ cảnh HTML, JavaScript, CSS, URL
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm:
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN JAVA: Dùng thư viện OWASP Java Encoder chuyên dụng
import org.owasp.encoder.Encode;

// 1. Ngữ cảnh HTML Body:
String safeBody = Encode.forHtml(userInput);

// 2. Ngữ cảnh HTML Attribute:
String safeAttr = Encode.forHtmlAttribute(userInput);

// 3. Ngữ cảnh JavaScript Block:
String safeJs = Encode.forJavaScript(userInput);

// 4. Ngữ cảnh URL Parameter:
String safeUrl = Encode.forUriComponent(userInput);
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tại sao việc dùng hàm escape HTML thông thường lại KHÔNG bảo vệ được khi dữ liệu được chèn vào bên trong thẻ