Một dữ liệu, năm ngữ cảnh, năm cách encode
XSS bị gộp vào A05 vì nó là injection — cùng cơ chế với SQL injection, khác interpreter. Nhưng có một điểm khiến nó khó hơn SQL injection về mặt thực hành, và đó là nội dung của bài này: HTML không phải một interpreter, nó là năm interpreter lồng nhau.
Cùng một chuỗi, đặt ở năm chỗ khác nhau trong một trang, cần năm cách xử lý khác nhau:
| Ngữ cảnh | Ví dụ | Cần gì |
|---|---|---|
| Thân phần tử | <div>HERE</div> | HTML entity encode |
| Giá trị thuộc tính | <div title="HERE"> | HTML entity encode và luôn có dấu nháy |
Trong <script> | var x = "HERE"; | JavaScript string encode — HTML encode ở đây là sai |
| Giá trị URL | <a href="/go?u=HERE"> | URL encode, cộng kiểm scheme |
Trong <style> | color: HERE; | CSS encode |
Điểm quan trọng nhất, và là chỗ framework không cứu được bạn: encode sai ngữ cảnh vừa không bảo vệ vừa làm hỏng dữ liệu. HTML-encode một chuỗi rồi đặt vào <script> cho ra " nằm trong code JavaScript — nó không chặn được </script> và nó làm chuỗi hiển thị sai.
Hai ngữ cảnh đáng nói riêng vì chúng là nguồn của gần như mọi XSS còn sót trong ứng dụng hiện đại:
(1) href và src. Encode không đủ, vì javascript:alert(1) không có ký tự nào cần encode. Ngữ cảnh này cần kiểm scheme: chỉ cho http, https, và có thể mailto. Đây là lỗi phổ biến nhất trong React — href={userUrl} không được React bảo vệ.
(2) DOM sink. innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, setTimeout với chuỗi, và dangerouslySetInnerHTML/v-html. Ở đây không có encode nào đúng — dữ liệu đi vào một parser HTML mới, nên cần sanitize (DOMPurify) hoặc đừng dùng sink đó.
Bình luận
Bình luận cần tài khoản đã hoàn thành ít nhất một bài học. Điều kiện đó là thứ giữ cho luồng thảo luận này còn đáng đọc: mỗi ý kiến gắn với một người có thể bị hỏi lại, và reputation tích luỹ theo thời gian.
Bạn vẫn đọc được toàn bộ bình luận dưới đây mà không cần tài khoản. Đăng nhập xong bạn sẽ quay lại đúng chỗ này, không phải đầu trang.
Đang tải bình luận…