- Lỗ hổng mới trong DOMPurify — HTML sanitizer được dùng bởi 24 triệu downloads/tuần — cho phép bypass sanitization qua prototype pollution.
- Ảnh hưởng phiên bản 3.0.1 tới 3.3.3, đã vá trong 3.4.0.
TL;DR
CVE-2026-41238 là một lỗ hổng Prototype Pollution → XSS trong DOMPurify, thư viện sanitize HTML phổ biến nhất của JavaScript ecosystem. Ảnh hưởng phiên bản 3.0.1 đến 3.3.3, đã vá trong 3.4.0. CVSS 6.9 (Moderate). Nếu app của bạn render HTML từ người dùng và dùng DOMPurify < 3.4.0, upgrade ngay: npm i dompurify@^3.4.0.
Có gì mới
Nhà nghiên cứu trace37 labs vừa công bố CVE-2026-41238, một bypass default-config của DOMPurify. Disclosure được điều phối qua Cure53 — team maintain DOMPurify. Full writeup kỹ thuật ở labs.trace37.com và advisory chính thức GHSA-v9jr-rg53-9pgp.
Điểm đáng chú ý: đây là CVE thứ 11 của DOMPurify trong 7 năm — một thành tích hiếm cho một thư viện bảo mật được dùng rộng rãi tới vậy, phản ánh chất lượng maintain của Cure53.
Tại sao quan trọng
DOMPurify không phải "một thư viện" — nó là de facto standard cho HTML sanitization trong JS. Theo npm, package này đạt 24,170,856 downloads mỗi tuần. Nó được embed trong React, Vue, Angular, Node.js pipelines, rich-text editors, markdown previewers, email clients, CMS, dashboard SaaS — bất cứ chỗ nào cần render HTML do user cung cấp.
Một bypass ở DOMPurify không chỉ là bug trong một lib: nó là lỗ hổng XSS tiềm tàng ở hàng nghìn sản phẩm downstream. Hệ quả: nếu app của bạn đã có sẵn một prototype-pollution gadget ở chỗ khác (phổ biến trong lodash cũ, jQuery, query-string parsers), kẻ tấn công có thể chain hai lỗ hổng để inject event handler vượt qua sanitizer.
Chi tiết kỹ thuật
Nguyên nhân gốc: ở dòng 590 trong purify.js, DOMPurify gán fallback cho config:
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};Plain object {} kế thừa từ Object.prototype. Nếu attacker đã pollute Object.prototype.tagNameCheck hoặc Object.prototype.attributeNameCheck bằng một regex permissive (ví dụ /.*/), DOMPurify sẽ đọc các value đó như config hợp lệ — và cho phép custom element kèm event handler lọt qua.
Kết quả: payload kiểu <x-foo onerror="alert(1)"> — vốn phải bị strip — lại được giữ nguyên trong output đã "sanitize".
Chuỗi tấn công trong thực tế gồm hai mắt xích. Mắt xích đầu là một pollution gadget sẵn có trên page, thường đi kèm query-string parser cũ hay lodash legacy, gán Object.prototype.tagNameCheck qua một URL crafted. Mắt xích thứ hai là lệnh DOMPurify.sanitize(userHtml) mà app gọi ngay sau đó — lúc này sanitizer đọc prototype đã bị pollute và "tin" rằng mọi custom tag kèm handler đều hợp lệ. Không có cảnh báo, không có exception, HTML độc lọt qua sạch sẽ.
Trace37 labs phát hiện bug bằng một evolutionary fuzzer, coi việc tìm bypass như bài toán tối ưu: payload được mutate, lai ghép, chấm điểm theo fitness function để tự tiến hoá về phía các path chưa được test. Cùng hệ thống đó từng cho ra CVE-2025-26791 trước đây.
| Thuộc tính | Giá trị |
|---|---|
| CVE | CVE-2026-41238 |
| GHSA | GHSA-v9jr-rg53-9pgp |
| CVSS v3.1 | 6.9 (Moderate) |
| Vector | AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:L/A:N |
| CWE | CWE-1321 + CWE-79 |
| Affected | DOMPurify 3.0.1 – 3.3.3 |
| Patched | DOMPurify 3.4.0 |
| Discovery | Evolutionary fuzzing (trace37 labs) |
So với các CVE trước
DOMPurify từng có vài CVE prototype-pollution đáng chú ý gần đây:
- CVE-2024-45801 / CVE-2024-48910 — tampering qua prototype pollution, bypass depth checking.
- GHSA-cj63-jhhr-wcxv — pollution bypass nhưng chỉ trigger khi bật
USE_PROFILES(opt-in). - CVE-2026-41238 (mới) — hit default configuration. Không cần opt-in flag, không cần cấu hình đặc biệt. Đây là điểm khiến nó nguy hiểm hơn nhóm trước.
Pattern vá cũng giống Cure53 đã làm trước đây: thay {} bằng Object.create(null) — prototype-less object — để vô hiệu hóa pollution vector ở tầng cấu trúc.
Ai bị ảnh hưởng
Ứng dụng có rủi ro cao khi thỏa cả hai điều kiện:
- Render HTML do người dùng cung cấp qua DOMPurify (3.0.1–3.3.3).
- Có một gadget prototype pollution riêng biệt trên page — thường từ dependency cũ (lodash < 4.17.21, jQuery < 3.4.0, legacy query-string parsers).
Ví dụ cụ thể: rich-text editors, markdown previewers (Mermaid từng bundle DOMPurify), email clients SaaS, forum, dashboard admin, feature chat có HTML formatting. SSR pipelines dùng isomorphic-dompurify cũng nằm trong scope.
Hạn chế & cách vá
Fix chính:
npm i dompurify@^3.4.0
# hoặc
pnpm up dompurify@latestSau khi upgrade, verify bằng npm ls dompurify để chắc chắn không còn phiên bản cũ nested trong dependency tree.
Mitigation bổ sung:
- Audit dependency có lịch sử prototype pollution (
npm audit, Snyk, Dependabot). - Nếu runtime cho phép, cân nhắc
Object.freeze(Object.prototype)ở bootstrap — chặn toàn bộ class pollution bug. - Áp dụng Content Security Policy chặt (script-src không có
'unsafe-inline') — defense-in-depth nếu sanitizer có lọt.
Caveat: CVSS Attack Complexity là High vì cần sẵn pollution gadget. Nhưng trong thực tế, gadget như vậy cực phổ biến, nên đừng dựa vào điều kiện này để trì hoãn vá.
Cái gì tiếp theo
Dự kiến đợt bump downstream: isomorphic-dompurify, Mermaid, jsPDF, và các framework integration sẽ release bản kèm DOMPurify 3.4.0 trong vài ngày tới. Trace37 labs đã úp hé lộ họ đang fuzz các path khác — khả năng cao sẽ có thêm CVE liên quan trong vài tháng tới. Theo dõi DOMPurify security advisories để nắm sớm.
Nguồn: GHSA-v9jr-rg53-9pgp, trace37 labs writeup, DOMPurify releases.
