§

Đầu vào

Chế độ
Kiểu entity
Phạm vi mã hóa
§

Đầu ra

Theo tiêu chuẩn TCVN 11930:2017 về lập trình ứng dụng web an toàn, mã hóa đầu ra HTML là yêu cầu bắt buộc để ngăn chặn tấn công XSS. Thông tư 03/2017/TT-BTTTT của Bộ Thông tin và Truyền thông quy định các cổng thông tin .gov.vn phải áp dụng output encoding cho mọi nội dung người dùng nhập vào. Sổ tay lập trình an toàn của BKAV dành cho startup SaaS Việt Nam cũng khuyến nghị dùng entity encoding kết hợp CSP để bảo vệ các ứng dụng web khỏi các lỗ hổng injection phổ biến.

Mã hóa HTML entity hoạt động như thế nào

HTML entity là tham chiếu ký tự mà trình duyệt phân tích thành một ký tự duy nhất. Năm ký tự HTML dành riêng (<, >, &, ", ') luôn cần mã hóa khi văn bản được hiển thị dưới dạng HTML; các ký tự còn lại là tùy chọn và phụ thuộc vào mã hóa tài liệu.

  1. Chọn chế độ và phạm vi. Chế độ mã hóa duyệt từng ký tự đầu vào. Chế độ giải mã duyệt đầu vào tìm kiếm các mẫu entity. Bộ chuyển đổi phạm vi quyết định chỉ mã hóa năm ký tự an toàn HTML hay cả các code point non-ASCII.
  2. Chọn kiểu entity. Entity có tên (&copy;) dễ đọc trong mã nguồn. Tham chiếu thập phân (&#169;) và hex (&#xA9;) mang mọi code point Unicode mà không cần tên. Các ứng dụng email cũ và parser XML ưu tiên dạng số.
  3. Duyệt đầu vào. Khi mã hóa, chúng tôi đọc từng code point và tra cứu trong bảng khoảng 200 entity có tên phổ biến. Những ký tự không có tên sẽ dùng dạng số. Khi giải mã, chúng tôi quét bằng một regex duy nhất khớp &name;, &#NNN;&#xHH; trong một lần.
  4. Ánh xạ thành ký tự. Entity có tên được tra cứu qua bảng ngược. Entity số được xử lý qua String.fromCodePoint với cơ số 10 hoặc 16. Entity có tên không xác định sẽ giữ nguyên để đầu vào một phần không bị mất dữ liệu.
  5. Chế độ trực tiếp. Bật chế độ trực tiếp và mỗi lần gõ sẽ chạy lại quá trình chuyển đổi với độ trễ 150 ms. Hữu ích khi bạn đang chỉnh sửa đoạn mã và muốn phản hồi ngay trước khi dán vào template.

Tại sao cần mã hóa HTML entity

  • Ngăn đầu vào người dùng làm hỏng bố cục. Khi người dùng nhập < vào ô bình luận, chèn thẳng văn bản đó vào HTML sẽ làm hỏng phần còn lại của trang. Mã hóa các ký tự dành riêng trước giúp trình duyệt hiển thị ký tự thay vì phân tích nó như phần đầu của thẻ.
  • Giữ giá trị thuộc tính hợp lệ. Nhúng chuỗi có dấu nháy vào thuộc tính HTML cần thay thế dấu nháy bằng &quot; (cho thuộc tính nháy kép) hoặc &#39; (cho thuộc tính nháy đơn). Nếu không, parser đóng thuộc tính sớm và phần còn lại trở thành markup lạc.
  • Vô hiệu hóa HTML trong dữ liệu lưu trữ. Log, báo cáo lỗi và xuất dữ liệu chat thường chứa dấu ngoặc nhọn và dấu và thực sự. Mã hóa entity trước khi dán vào trang tài liệu giúp nội dung đó hiển thị dưới dạng văn bản thay vì kích hoạt renderer hoặc bộ phát hiện liên kết tự động.
  • Chia sẻ đoạn mã an toàn. Đăng thẻ ví dụ như <script>alert(1)</script> trong bài blog, email hoặc tin nhắn Slack cần mã hóa dấu ngoặc để đoạn mã hiển thị thay vì chạy. Kỹ thuật tương tự áp dụng cho nội dung RSS feed và trường `description` trong JSON-LD.

Ứng dụng phổ biến

Mã hóa entity xuất hiện ở bất cứ đâu văn bản thô được kết hợp vào HTML lúc runtime — ngay cả khi framework thường xử lý cho bạn, công cụ thủ công vẫn hữu ích trong những trường hợp ngoại lệ.

  • Template server-rendered: Jinja2, ERB, Twig và Handlebars tự động escape theo mặc định, nhưng các khối raw và marker `safe` tắt tính năng đó — codec giúp bạn xác nhận kết quả escape sẽ tạo ra gì.
  • Soạn thảo email và newsletter: nhiều engine template ESP không tự escape các trường merge, vì vậy smart quote và ký hiệu bản quyền trong tên do người dùng cung cấp cần mã hóa trước.
  • Tài liệu và mẫu code: dán thẻ HTML ví dụ vào bài blog Markdown hoặc đoạn mã static site cần mã hóa dấu ngoặc để renderer xử lý nó như văn bản hiển thị.

Ví dụ minh họa

Dán <script>alert('hi')</script> vào đầu vào với chế độ Encode, kiểu Named, phạm vi Minimal. Đầu ra là &lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;. Chuyển kiểu sang Numeric hex và đầu vào tương tự tạo ra &#x3C;script&#x3E;alert(&#x27;hi&#x27;)&#x3C;/script&#x3E;. Chuyển sang chế độ Decode, dán chuỗi đã mã hóa vào và thẻ gốc sẽ trở lại nguyên vẹn.

FAQ

HTML entity là gì?

HTML entity là tham chiếu ký tự mà trình duyệt thay thế thành ký tự đơn khi phân tích trang. Chúng có ba dạng: named (như &amp; cho &), số thập phân (&#38;) và số hex (&#x26;). Năm ký tự HTML dành riêng (<, >, &, ", ') cần mã hóa bất cứ khi nào văn bản được chèn vào HTML. Khoảng 2.225 entity có tên còn lại bao gồm ký hiệu, dấu thanh và chữ Hy Lạp nhưng là tùy chọn khi mã hóa tài liệu là UTF-8.

Khi nào dùng entity có tên vs. entity số?

Dùng entity có tên khi bạn muốn mã nguồn dễ đọc (người xem &copy; trong template hiểu ngay). Dùng dạng số (thập phân hoặc hex) khi đối tượng tiêu thụ cũ hơn hoặc nghiêm ngặt hơn — parser XML, ứng dụng email cũ và một số feed reader chỉ nhận dạng tập con nhỏ của entity có tên HTML5, nhưng đều nhận dạng dạng số. Hex thường thắng trong bối cảnh bảo mật vì nó khớp một-một với ký hiệu code point Unicode trong tài liệu đặc tả.

Giải mã có xử lý entity hex như & không?

Có. Bộ giải mã dùng một regex duy nhất khớp cả ba dạng entity trong một lần: &name;, &#NNN;&#xHH;. Các entity số được giải quyết bằng String.fromCodePoint dùng cơ số 10 hoặc 16. Đầu vào hỗn hợp (named và numeric trong cùng chuỗi) được giải mã đúng, và entity có tên không xác định giữ nguyên để đầu vào một phần không mất dữ liệu.

Có an toàn để dùng với đầu vào không đáng tin cậy không?

Bản thân codec chỉ chạy trên trình duyệt và không gửi đầu vào của bạn đến bất kỳ đâu. Đầu ra có an toàn để nhúng hay không phụ thuộc vào ngữ cảnh. Mã hóa entity xử lý ngữ cảnh HTML body và attribute-value, bao gồm trường hợp OWASP Rule #1. Ngữ cảnh JavaScript (inline event handler, khối `<script>`), ngữ cảnh CSS và URL mỗi loại cần quy tắc mã hóa riêng — entity encoding một mình là không đủ. Để bảo vệ phía server theo chiều sâu, kết hợp với engine template nhận biết ngữ cảnh như DOMPurify hoặc tính năng auto-escape của framework.

Mã hóa entity phía trình duyệt nằm ở ranh giới giữa đầu vào người dùng và HTML được hiển thị. Thực hiện chuyển đổi cục bộ cho phép bạn kiểm tra kết quả framework sẽ tạo ra mà không cần gửi văn bản gốc đến công cụ bên thứ ba.