5 điểm bởi GN⁺ 2026-02-26 | 2 bình luận | Chia sẻ qua WhatsApp
  • Để ngăn chặn tấn công XSS — một trong những lỗ hổng lớn nhất của web — Firefox là trình duyệt đầu tiên hỗ trợ Sanitizer API được chuẩn hóa
  • Khi dùng phương thức setHTML() thay cho innerHTML trước đây, HTML không đáng tin cậy sẽ được tự động làm sạch (sanitize) trước khi chèn vào DOM, từ đó loại bỏ script độc hại
  • Nếu cấu hình mặc định quá chặt hoặc chưa đủ, nhà phát triển có thể kiểm soát các phần tử và thuộc tính được cho phép thông qua thiết lập tùy chỉnh
  • Tính năng này của Firefox khi kết hợp với Trusted Types sẽ nâng cao mức độ bảo mật của web nói chung, đồng thời giúp nhà phát triển có thể ngăn chặn XSS mà không cần một đội ngũ bảo mật riêng

Lỗ hổng XSS và cách Firefox ứng phó

  • Cross-site scripting (XSS) xảy ra khi kẻ tấn công chèn HTML hoặc JavaScript tùy ý thông qua nội dung do người dùng nhập
    • Kẻ tấn công có thể lợi dụng điều này để theo dõi tương tác của người dùng hoặc đánh cắp dữ liệu
    • XSS đã được xếp vào top 3 lỗ hổng web nghiêm trọng (CWE-79) trong gần 10 năm
  • Từ năm 2009, Firefox đã dẫn dắt tiêu chuẩn Content-Security-Policy (CSP) để tăng cường phòng vệ trước XSS
    • CSP giới hạn các tài nguyên mà website có thể tải và thực thi
    • Tuy nhiên, vì đòi hỏi thay đổi cấu trúc website hiện có và rà soát bảo mật liên tục nên việc áp dụng rộng rãi còn hạn chế

Vai trò của Sanitizer API và setHTML()

  • Sanitizer API cung cấp một phương thức chuẩn hóa để chuyển đổi HTML độc hại thành dạng vô hại
    • Trong ví dụ mã, phần tử <img src="x" onclick="alert('XSS')"> bị loại bỏ và chỉ còn lại <h1>Hello my name is</h1>
  • Phương thức setHTML() tự động thực hiện quá trình làm sạch khi chèn HTML, bảo đảm hành vi an toàn theo mặc định
    • Chỉ cần thay phép gán innerHTML hiện tại bằng setHTML() là đã có thể có cơ chế phòng vệ XSS mạnh mẽ
  • Nếu thiết lập mặc định quá nghiêm ngặt hoặc quá lỏng, nhà phát triển có thể định nghĩa các phần tử và thuộc tính HTML được cho phép bằng cấu hình tùy chỉnh
    • Có thể dùng công cụ Sanitizer API playground để thử nghiệm

Kết hợp với Trusted Types

  • Trusted Types API cung cấp thêm một lớp bảo mật bằng cách kiểm soát tập trung việc phân tích và chèn HTML
    • Khi dùng setHTML(), có thể áp dụng chính sách Trusted Types một cách dễ dàng
    • Chính sách nghiêm ngặt có thể chỉ cho phép setHTML() và chặn các cách chèn nguy hiểm khác, góp phần ngăn XSS tái xuất trong tương lai

Tác động nâng cao bảo mật trong Firefox 148

  • Firefox 148 hỗ trợ cả Sanitizer APITrusted Types, qua đó nâng cao đáng kể mức bảo mật mặc định
  • Nhà phát triển có thể ngăn chặn XSS chỉ với thay đổi mã đơn giản, không cần chính sách bảo mật phức tạp hay đội ngũ bảo mật riêng
  • Việc đưa tiêu chuẩn này vào trình duyệt được kỳ vọng sẽ thúc đẩy một môi trường web an toàn hơn trên mọi trình duyệt

Tóm tắt

  • Firefox 148 hỗ trợ nhà phát triển web dễ dàng chặn tấn công XSS thông qua phương thức setHTML() và Sanitizer API
  • Tính năng này khắc phục các giới hạn của CSP và là bước đệm để phổ biến cách chèn HTML an toàn theo mặc định như một tiêu chuẩn web
  • Việc kết hợp với Trusted Types cho phép duy trì bảo mật lâu dài và ngăn XSS tái diễn
  • Kết quả là Firefox đang dẫn dắt quá trình chuyển đổi sang một môi trường web nơi bảo mật là mặc định

2 bình luận

 
huiya 2026-02-26

Ồ đúng là kiểu này thực sự cần thiết. Nếu được hỗ trợ trên mọi trình duyệt thì có lẽ sẽ tuyệt vời lắm.

 
GN⁺ 2026-02-26
Ý kiến trên Hacker News
  • Những tính năng kiểu này lúc nào cũng hơi đáng lo
    Vì có sự pha trộn giữa các phương thức xử lý an toàn khi nhận đầu vào người dùng tùy ý và các phương thức không an toàn, nhưng chỉ nhìn tên thì khó phân biệt
    Lý tưởng nhất là ngay từ đầu, các hàm nguy hiểm phải được thể hiện rõ trong tên gọi
    Ngoài ra, chính khái niệm “sanitize” HTML cũng khá mơ hồ, và khó đánh giá liệu nó có thực sự an toàn hay không

    • Đúng là định nghĩa của “an toàn” còn mơ hồ, nhưng mục tiêu ở đây là an toàn trước XSS
      Nó loại bỏ các phần tử hoặc thuộc tính có thể thực thi script, và logic này chạy bên trong engine trình duyệt nên xử lý chính xác hơn các sanitizer dựa trên chuỗi
      Xem thêm trong tài liệu MDN về setHTML
    • Thực ra đã có sự phân biệt khá rõ ràng rồi
      elementNode.textContent an toàn với đầu vào không đáng tin cậy, còn elementNode.innerHTML thì không
      Cái trước escape mọi ký tự, còn cái sau thì không escape gì cả
      Cũng có ý kiến cho rằng “HTML sanitization” là vấn đề về bản chất không thể giải quyết triệt để
      Xem thảo luận liên quan ở bình luận này
      Các API kiểu này lẽ ra không nên được thông qua ngay từ giai đoạn đề xuất
    • Thay vì để innerHTMLsetHTML cùng tồn tại, lẽ ra nên loại bỏ hoàn toàn innerHTML, và nếu cần hành vi cũ thì dùng setHTMLUnsafe
    • Sẽ tốt hơn nếu nhà phát triển web có thể vô hiệu hóa các API cũ như innerHTML bằng một thiết lập toàn cục
      Tuy nhiên làm vậy có thể khiến website không hoạt động trên các trình duyệt cũ
    • Nếu trang được phục vụ kèm header Content-Security-Policy: require-trusted-types-for 'script', thì có thể chặn việc truyền chuỗi thông thường vào các phương thức không có sanitizer
  • Nếu như ví dụ cho thấy có thể chèn các thẻ như <h1> hay <br> vào tên người dùng, thì dù có chặn thực thi script đi nữa vẫn vẫn có thể tiêm markup tùy ý
    Thậm chí còn có thể thay đổi CSS bằng thẻ <style>, ví dụ làm biến đổi giao diện của trang hồ sơ PayPal
    Điều đó khiến người ta phải tự hỏi ai lại muốn như vậy

    • Dù vậy, điều này vẫn có thể hữu ích khi muốn cho người dùng dùng Markdown, như trên diễn đàn
      Có thể thêm một lớp phòng vệ bằng cách tiếp tục giới hạn HTML sinh ra từ Markdown bằng sanitizer để chỉ cho phép một số thẻ nhất định
    • Trong trường hợp đó lẽ ra nên dùng innerHTML mà không phải innerText hay textContent
      setHTML là phương án thay thế cho innerHTML
    • Nếu cấu hình mặc định của setHTML() quá chặt hoặc quá lỏng, nhà phát triển có thể cung cấp cấu hình tùy chỉnh để tự định nghĩa các phần tử và thuộc tính HTML được phép
    • Chỉ riêng CSS cũng có thể tạo ra rủi ro bảo mật, nên vẫn cần cẩn trọng
      Xem thảo luận liên quan ở chuỗi này
    • Ví dụ
      .setHTML("<h1>Hello</h1>", new Sanitizer({}))
      
      Làm như vậy sẽ loại bỏ toàn bộ phần tử
      Cuối cùng thì ở backend vẫn cần sanitize tên người dùng theo cách tiêu chuẩn, và khi xuất ra phải áp dụng HTML escape
      Theo RFC 2119, đây là yêu cầu ở mức “SHOULD”
  • Rất vui khi thấy tính năng này xuất hiện, nhưng có lẽ sẽ mất thời gian để mức độ hỗ trợ của trình duyệt đủ phổ biến
    Có thể kiểm tra tình trạng hỗ trợ trên Can I use

    • Như các API trình duyệt khác, có thể phải mất vài năm, hoặc nếu chỉ nhắm đến bản mới nhất thì có thể chỉ vài tháng
      Trong thời gian đó có thể dùng polyfill để thay thế
  • Tiêu đề hơi giật gân một chút
    Thực ra cũng có thể triển khai sanitization bằng một hàm kiểm tra đầu vào trước khi đưa vào innerHTML
    Tuy vậy, những nỗ lực kiểu này rốt cuộc vẫn tạo cảm giác như đang phát minh lại bánh xe
    Ngoài ra, trên Firefox cũ thì hacks.mozilla.org thậm chí không mở được, còn trên Pale Moon hay SeaMonkey thì MDN bị hiển thị lỗi
    Cứ như thể “liên minh trình duyệt” đang cố phá hỏng web vậy

    • Nói rằng “có thể giải quyết bằng hàm kiểm tra đầu vào” cũng giống như nói “C vẫn an toàn bộ nhớ nếu không có bug”
      Đây là lập luận còn chưa tính đến vấn đề khác biệt giữa các parser (parser differential)
  • Nếu dùng sai Sanitizer API thì nó có thể trở thành footgun
    Đặc biệt phải cẩn thận khi dùng chế độ “remove”
    Tôi nghĩ tốt hơn là chỉ dùng setText, và hoàn toàn không cho phép người dùng thêm HTML

    • Dùng Sanitizer theo kiểu allowlist sẽ giảm rủi ro, nhưng miễn là dùng setHTML thì sẽ không phát sinh XSS
    • Nhưng nếu tác giả trang cần thêm các mảnh HTML lớn thì sao
      Nhìn vào thực tế innerHTML được dùng thường xuyên, rất khó loại bỏ hoàn toàn
    • Thậm chí những API kiểu này còn có thể tạo ra ảo giác rằng nó “an toàn 100%”, và vì thế lại càng nguy hiểm hơn
  • Điều gây ấn tượng là mọi khía cạnh của truy cập mạng giờ đây đều được kiểm soát đúng cách, nên chuỗi bảo mật đã chuyển từ niềm tin vào mã nguồn sang niềm tin vào cấu hình máy chủ
    Các giá trị mặc định cũng được thiết lập an toàn

  • Điều tôi thực sự muốn là một phần tử <sandbox> có thể chạy mã nguy hiểm một cách an toàn
    Không phải sửa mã nguy hiểm, mà là cho phép chạy nó trong môi trường cô lập
    iframe có hạn chế là không thể chảy cùng DOM, và trong thời đại AI cùng nội dung động ngày càng nhiều, chúng ta cần khả năng đóng gói có thể cấu hợp

  • Tôi thực sự thích cái tên setHTMLUnsafe
    Các tính năng bảo mật sẽ thất bại nếu được thiết kế theo kiểu bắt nhà phát triển phải opt-in
    Thay vào đó, cách hiệu quả hơn là khiến “con đường nguy hiểm trông thật sự nguy hiểm”

  • Cái tên set_html() trực quan hơn nhiều so với inner_html
    API của JavaScript thực sự quá lộn xộn và đến lúc nào đó cần được dọn dẹp
    Thảo luận lần này tập trung vào bảo mật, nhưng khi công bố API mới thì bản thân thiết kế cũng phải gọn gàng

    • Nói chính xác hơn thì đây là DOM API
      DOM API từ trước đến nay, và cả bây giờ nữa, vẫn tạo cảm giác như được làm ra bởi “những người chưa từng thiết kế API”
  • Lập trình viên thập niên 90:

    SQL("select * from user where name = " + name);
    

    Lập trình viên thập niên 2020:

    div.innerHTML = "Hello " + user.name;
    
    • Lập trình viên thập niên 2030:
      "Summarize this email: " + email.contents
      
      Prompt injection chỉ là cùng một vấn đề cũ trên nền công nghệ mới
      Chúng ta đã chẳng học được gì từ thập niên 90