Để ngăn chặn XSS, quan trọng nhất là: không bao giờ tin dữ liệu từ người dùng và luôn xử lý (encode / validate) trước khi hiển thị ra HTML / JS / URL. Mình tóm tắt theo kiểu “checklist” cho bạn dễ áp dụng trong code.
1. Hiểu nhanh XSS là gì
XSS xảy ra khi attacker chèn được một đoạn script (thường là JavaScript) vào trang web của bạn, và đoạn script đó chạy trên trình duyệt của người dùng khác.
Ví dụ: user nhập vào <script>alert('XSS')</script> mà bạn hiển thị lại thô trong HTML => script chạy.
2. Nguyên tắc vàng: Output Encoding (Escape đúng ngữ cảnh)
Khi hiển thị dữ liệu ra HTML, phải encode ký tự đặc biệt:
-
Trong HTML content: encode
< > & " '-
Ví dụ (pseudo):
String safe = HtmlUtils.htmlEscape(userInput); out.println(safe);
-
-
Trong HTML attribute:
<input value="<!-- phải escape ở đây -->"> -
Trong JavaScript context:
<script> var msg = "<!-- phải escape để thành chuỗi JS hợp lệ -->"; </script> -
Trong URL / query string:
<a href="/search?q=<!-- phải URL-encode -->">Link</a>
👉 Mỗi framework đều có cách encode sẵn:
- React/Angular/Vue: binding bình thường thường đã escape HTML (trừ khi bạn cố tình dùng
dangerouslySetInnerHTML,v-html,innerHTML…). - Template engine (Blade, Thymeleaf, JSP, EJS…): thường có
{{ }}(escaped) và{!! !!}hoặc tương tự (raw). Luôn ưu tiên escaped.
3. Không cho phép HTML tùy ý (hoặc sanitize cẩn thận)
Nếu tính năng của bạn cho phép user nhập HTML/Rich text (editor WYSIWYG, comment có format, v.v.), thì:
-
Tránh: lưu thô HTML rồi render lại trực tiếp.
-
Nếu bắt buộc cho HTML:
- Dùng HTML sanitizer (loại bỏ
<script>,onerror,onload,javascript:…) - Chỉ cho phép một whitelist tag an toàn:
<b>, <i>, <u>, <p>, <ul>, <li>, <a>...
- Dùng HTML sanitizer (loại bỏ
Ví dụ thư viện (tùy tech stack):
- JS: DOMPurify
- Java: OWASP Java HTML Sanitizer
- PHP: HTML Purifier
4. Không cho user chèn thẳng vào JavaScript
Tránh pattern kiểu:
<script>
var name = '<?= $_GET["name"] ?>'; // RẤT nguy hiểm
</script>
Nếu cần, hãy:
-
Encode chuỗi thành JSON rồi parse:
<script> const user = JSON.parse('<?= json_encode($user, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>'); </script>
Hoặc lấy dữ liệu qua API (JSON) rồi render bằng JS, không nhét trực tiếp HTML từ server vào script.
5. Bật và cấu hình Content Security Policy (CSP)
CSP giúp giảm thiểu thiệt hại nếu bị chèn XSS:
-
Cấu hình header
Content-Security-Policy, ví dụ đơn giản:Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; -
Tránh dùng
unsafe-inlinetrừ khi rất cần. -
Tách code JS ra file
.jsriêng, hạn chế<script>inline</script>.
CSP không thay thế cho việc encode, nhưng là layer phòng thủ thêm.
6. Bật HttpOnly, Secure cho cookie (bảo vệ session)
XSS thường được dùng để đánh cắp session cookie. Hãy:
-
Đặt cookie auth với flag:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax -
HttpOnly: JS không đọc được cookie → giảm nguy cơ bịdocument.cookieăn cắp. -
Secure: chỉ gửi qua HTTPS. -
SameSite: giảm rủi ro CSRF.
Lưu ý: HttpOnly không ngăn được XSS, nhưng làm giảm thiệt hại nếu bị XSS.
7. Validate input, nhưng đừng nhầm với encode
Input validation không đủ để ngăn XSS nhưng vẫn cần:
- Chặn những pattern rõ ràng nguy hiểm nếu không cần (tag, thuộc tính, protocol
javascript:). - Giới hạn độ dài, loại ký tự cho từng field (VD: username chỉ cho [a-zA-Z0-9_], không cần
< >).
Tuy nhiên, phòng XSS cốt lõi vẫn là output encoding theo ngữ cảnh.
Validation là lớp phụ, không phải “chủ lực”.
8. Không tin dữ liệu từ database / API nội bộ
Rất nhiều người nghĩ:
“Dữ liệu trong DB là sạch vì đã lưu rồi.”
Sai. Nếu lúc insert không encode, attacker chỉ cần inject 1 lần → dữ liệu độc đã nằm trong DB.
Nên khi output từ DB ra HTML vẫn phải encode.
Rule:
BẤT CỨ THỨ GÌ ĐI RA HTML/JS/URL → encode theo ngữ cảnh, bất kể nó đến từ đâu (form, DB, log, API khác…)
9. Hạn chế dùng innerHTML, v-html, dangerouslySetInnerHTML
Ở front-end:
- Tránh
element.innerHTML = userInput; - React: tránh
dangerouslySetInnerHTML. - Vue: hạn chế
v-html. - Nếu buộc phải dùng, hãy sanitize nội dung trước (DOMPurify…).
Thay vào đó:
- Dùng binding text:
textContent,{{ variable }}, v.v.
10. Kiểm thử và sử dụng các payload XSS mẫu
Khi muốn test hệ thống có bị XSS không, bạn có thể thử vài payload đơn giản:
<script>alert(1)</script>
"><script>alert(1)</script>
"><img src=x onerror=alert(1)>
Nếu những thứ này xuất hiện nguyên dạng như text → tốt.
Nếu trình duyệt thực thi cảnh báo → có lỗ hổng XSS.
11. Tổng kết theo kiểu checklist cho dev
Khi code, hãy tự check:
- [ ] Tất cả dữ liệu từ user/DB/3rd-party khi render ra HTML đều được encode đúng ngữ cảnh?
- [ ] Có dùng template engine / framework binding mặc định (escape HTML) không?
- [ ] Có dùng
innerHTML,v-html,dangerouslySetInnerHTML→ nếu có, đã sanitize chưa? - [ ] Đã cấu hình CSP, HttpOnly/Secure/SameSite cho cookie auth chưa?
- [ ] Các input có được validate cơ bản (độ dài, loại ký tự, whitelist) không?
- [ ] Có test XSS với vài payload basic chưa?

