Bài học bảo mật từ Scratch: Tại sao việc "làm sạch" SVG là vô vọng?

27 tháng 4, 2026·6 phút đọc

Lịch sử đầy rẫy lỗ hổng bảo mật liên quan đến SVG trên nền tảng Scratch cho thấy một sự thật đáng báo động: việc cố gắng lọc bỏ mã độc từ SVG là một cuộc chiến không thể thắng. Bài viết này phân tích chuỗi các lỗi từ XSS đến rò rỉ HTTP trong suốt 7 năm qua và đề xuất giải pháp sandboxing bằng iframe thay vì các bộ lọc phức tạp.

Bài học bảo mật từ Scratch: Tại sao việc "làm sạch" SVG là vô vọng?

Bài học bảo mật từ Scratch: Tại sao việc "làm sạch" SVG là vô vọng?

Scratch, nền tảng lập trình nổi tiếng dành cho trẻ em, có một lịch sử lâu dài về các lỗ hổng bảo mật liên quan đến SVG (Scalable Vector Graphics). Nguồn gốc của vấn đề nằm ở cách Scratch xử lý nội dung do người dùng tạo: nền tảng này phân tích cú pháp (parse) SVG và chèn nó trực tiếp vào tài liệu chính để thực hiện các thao tác như đo kích thước bounding box.

Dù thời gian SVG tồn tại trong tài liệu chính ngắn đến đâu, đây vẫn là một hoạt động vốn dĩ không an toàn. Trong nhiều năm, Scratch đã cố gắng giải quyết vấn đề này bằng cách xây dựng cơ sở hạ tầng ngày càng phức tạp để phân tích và loại bỏ các phần nguy hiểm trong mã đánh dấu (markup). Tuy nhiên, thực tế đã chứng minh rằng cách tiếp cận này đang đi vào ngõ cụt.

Hình ảnh minh họa bảo mậtHình ảnh minh họa bảo mật

Lịch sử các lỗ hổng và các bản vá lỗi không hồi kết

Hành trình khắc phục các lỗi SVG của Scratch giống như một trò chơi "đập chuột", nơi cứ vá một lỗ hổng thì lại xuất hiện một lỗ hổng khác.

2019: XSS qua thẻ <script>

Ngay sau khi Scratch 3 ra mắt, một lỗ hổng XSS (Cross-Site Scripting) được phát hiện. Kẻ tấn công có thể nhúng thẻ <script> vào SVG, và khi SVG tải, mã độc sẽ được thực thi. Điều này cho phép kẻ tấn công đăng bình luận, xóa dự án hoặc chiếm quyền tài khoản nạn nhân. Bản sửa lỗi lúc đó sử dụng biểu thức chính quy (regex) để loại bỏ thẻ script.

2020: Regex thất bại (CVE-2020-27428)

Vào năm 2020, người ta phát hiện ra rằng bản vá trước đó hoàn toàn vô dụng. Regex có phân biệt chữ hoa chữ thường, nên chỉ cần viết <Script> là có thể vượt qua. Hơn nữa, còn có nhiều cách khác để nhúng JavaScript, chẳng hạn như trình xử lý sự kiện nội tuyến (inline event handler). Scratch đã chuyển sang sử dụng thư viện DOMPurify để làm sạch SVG.

2022: Rò rỉ HTTP qua thuộc tính href

DOMPurify tốt trong việc chặn mã thực thi, nhưng không ngăn chặn được rò rỉ HTTP. Kẻ tấn công có thể sử dụng thuộc tính href trong thẻ <a> để kích hoạt một yêu cầu tới máy chủ bên ngoài khi SVG được tải. Điều này cho phép chúng ghi lại địa chỉ IP của người xem dự án, từ đó suy ra vị trí địa lý hoặc khu vực trường học. Scratch đã thêm các hook vào DOMPurify để chặn các URL bên ngoài.

2023: Rò rỉ qua CSS @import

Kẻ tấn công tìm ra cách sử dụng câu lệnh @import bên trong thẻ <style> để gọi tài nguyên bên ngoài. Scratch buộc phải tích hợp một trình phân tích cú pháp CSS bằng JavaScript để loại bỏ các phần nguy hiểm trong CSS.

2024: XSS qua Paper.js

Một lỗ hổng XSS khác lại xuất hiện thông qua thư viện Paper.js được sử dụng trong trình chỉnh sửa trang phục (costume editor). Vấn đề là SVG chưa được làm sạch đã được truyền trực tiếp cho Paper.js.

2025 - 2026: Cuộc chiến không hồi kết với CSS

Các lỗ hổng tiếp tục xuất hiện liên tục thông qua các tính năng CSS như url(), biến CSS (var(--name)), và các mã thoát (escape codes). Kẻ tấn công còn tìm ra cách sử dụng image-set() để gây rò rỉ HTTP. Thậm chí, một lỗ hổng nghiêm trọng hơn cho phép kẻ tấn công áp dụng kiểu dáng (style) cho toàn bộ trang Scratch, có thể ẩn nút báo cáo hoặc tạo các nút giả mạo để lừa người dùng (phishing).

Minh họa cấu trúc bảo mậtMinh họa cấu trúc bảo mật

Tại sao cách tiếp cận này thất bại?

Việc liên tục thêm các lớp phức tạp vào quy trình làm sạch (sanitization) rõ ràng là một cách tiếp cận doomed (định mệnh đã an bài). Chúng ta đã đi sâu hơn 5 bản sửa đổi lớn nhưng vẫn còn những lỗ hổng chưa vá.

Có ba lý do chính khiến phương pháp này không bền vững:

  1. Sự không khớp giữa trình phân tích cú pháp: Thư viện css-tree mà Scratch sử dụng để phân tích CSS có thể không khớp hoàn toàn với trình phân tích CSS thực tế trong trình duyệt. Nếu css-tree không hiểu một cú pháp mới, nó sẽ bỏ qua, trong khi trình duyệt lại thực thi nó.
  2. Các tính năng CSS mới: Trình duyệt liên tục thêm các hàm mới có thể tham chiếu nội dung bên ngoài (như src(), image() trong các thông số kỹ thuật mới). Việc theo kịp mọi thay đổi để đánh giá xem chúng có nguy hiểm hay không là bất khả thi.
  3. Phức tạp quá mức: Mã nguồn xử lý làm sạch đã trở nên quá phức tạp, dẫn đến việc bỏ sót các trường hợp biên (edge cases) như cú pháp lồng nhau (nesting) của CSS.

Giải pháp thay thế: Sandbox và Iframe

Thay vì cố gắng lọc bỏ mọi thứ "xấu", TurboWarp (một bản fork của Scratch mà tôi đang phát triển) đã chọn một hướng đi khác: cô lập (sandbox) SVG bên trong một iframe.

Cách tiếp cận này hoạt động như sau:

  1. Tạo một iframe với thuộc tính sandbox="allow-same-origin". Điều này chặn thực thi script bên trong iframe nhưng vẫn cho phép tương tác với nội dung.
  2. Sử dụng Content-Security-Policy (CSP) nội tuyến để chặn tất cả các script và chỉ cho phép tải tài nguyên an toàn từ data URL.
  3. Đặt iframe ở ngoài màn hình (offscreen) để các API đo lường vẫn hoạt động.

Giải pháp sandboxGiải pháp sandbox

Phương pháp này mang lại những lợi ích to lớn:

  • Trình duyệt làm việc nặng: Chúng ta tận dụng mã nguồn có sẵn của trình duyệt để xử lý bảo mật.
  • Tương lai an toàn: TurboWarp không cần biết mọi cách mà SVG có thể gửi yêu cầu. Trình duyệt đã biết điều đó và sẽ thực thi nó cho bất kỳ API mới nào được thêm vào sau này.
  • Cô lập hoàn toàn: SVG không thể ảnh hưởng đến tài liệu chính. Ví dụ, cuộc tấn công "restyling toàn bộ trang" sẽ chỉ làm thay đổi giao diện bên trong iframe, nơi không ai nhìn thấy.

Kể từ khi áp dụng giải pháp này, TurboWarp đã tránh được các lỗ hổng rò rỉ HTTP năm 2026 và vấn đề restyling giao diện, thậm chí chúng tôi còn giảm được 400KB dung lượng gói phần mềm bằng cách xóa bỏ mã làm sạch CSS phức tạp.

Kết luận

Câu chuyện của Scratch là một lời cảnh tỉnh cho các nhà phát triển web. Việc cố gắng làm sạch dữ liệu người dùng phức tạp như SVG bằng các bộ lọc thủ công là một cuộc chiến thua trước. Hãy để trình duyệt và các cơ chế bảo mật chuẩn như CSP, iframe sandbox làm công việc khó khăn đó thay cho bạn. Đó mới là cách tiếp cận bền vững để bảo vệ người dùng trước những mối đe dọa ngày càng tinh vi trên web.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗