1 điểm bởi GN⁺ 2025-12-14 | 1 bình luận | Chia sẻ qua WhatsApp
  • An toàn bộ nhớsandboxing là hai khái niệm bảo mật độc lập với nhau, và cần có cả hai để hình thành một cơ chế phòng vệ mạnh mẽ
  • Fil-C là một triển khai an toàn bộ nhớ cho C/C++, bảo đảm tính an toàn tới cấp độ system call của Linux và có thể được sử dụng cả trong các thành phần hệ thống như OpenSSH
  • Trong quá trình chuyển sandbox dựa trên seccomp-BPF của OpenSSH sang Fil-C, việc hạn chế tạo luồng và điều chỉnh bộ lọc seccomp là các thách thức then chốt
  • Để quản lý các luồng nền của runtime Fil-C, API zlock_runtime_threads() đã được bổ sung nhằm kiểm soát hoạt động của luồng bên trong sandbox
  • Fil-C triển khai việc áp dụng đồng bộ lệnh gọi prctl tới mọi luồng runtime, để no_new_privsbộ lọc seccomp được áp dụng nhất quán trên toàn bộ tiến trình

Mối quan hệ giữa an toàn bộ nhớ và sandboxing

  • An toàn bộ nhớ và sandboxing là các lớp bảo mật khác nhau, và chỉ một bên thôi thì không thể cung cấp sự bảo vệ hoàn chỉnh
    • Ví dụ an toàn bộ nhớ nhưng không có sandbox: chương trình Java có thể ghi đè tệp thông qua đầu vào người dùng
    • Ví dụ có sandbox nhưng không an toàn bộ nhớ: chương trình viết bằng assembly với quyền bị giới hạn
  • Sandbox thực tế vẫn tồn tại lỗ hổng mang tính cấu trúc, chẳng hạn như cho phép giao tiếp với tiến trình broker
  • Vì vậy, kết hợp an toàn bộ nhớ và sandboxing là cách phòng vệ tốt nhất

Kết hợp Fil-C với sandbox OpenSSH

  • Fil-C là một triển khai an toàn bộ nhớ cho C/C++, duy trì tính an toàn ở cấp độ system call của Linux
    • Runtime Fil-C có thể chạy cả trong các thành phần hệ thống cấp thấp như init, udevd
    • OpenSSH hoạt động bình thường trên Fil-C và sử dụng sandbox seccomp-BPF
  • Trên Linux, OpenSSH xây dựng sandbox bằng các công cụ sau
    • Hạn chế truy cập hệ thống tệp bằng chroot
    • Chạy bằng người dùng/nhóm sshd
    • Giới hạn mở tệp và tạo tiến trình bằng setrlimit
    • Chỉ cho phép các system call được chỉ định bằng bộ lọc seccomp-BPF
  • Fil-C hỗ trợ sẵn chroot và chuyển đổi người dùng, nhưng setrlimit và seccomp-BPF có thể xung đột với hoạt động của runtime nên cần điều chỉnh bổ sung

Kiểm soát luồng trong runtime Fil-C

  • Runtime Fil-C sử dụng các luồng nền cho garbage collection và tự động dừng/khởi động lại khi cần
  • Sandbox setrlimit của OpenSSH nhằm mục tiêu cấm tạo tiến trình mới, nên việc runtime tạo luồng có thể vi phạm điều này
  • Để giải quyết, API zlock_runtime_threads() đã được thêm vào <stdfil.h>
    • Runtime sẽ tạo ngay các luồng cần thiết và sau đó vô hiệu hóa việc tự động kết thúc
    • Được gọi trong hàm ssh_sandbox_child của OpenSSH trước khi gọi setrlimit hoặc seccomp-BPF

Điều chỉnh bộ lọc seccomp của OpenSSH

  • Sau khi áp dụng zlock_runtime_threads(), phần lớn chức năng sandbox vẫn hoạt động như cũ
  • Các thay đổi sau đã được thực hiện trong bộ lọc seccomp
    • Khi vi phạm, đặt thành SECCOMP_RET_KILL_PROCESS để các luồng nền của Fil-C cũng bị chấm dứt cùng lúc
    • Thêm MAP_NORESERVE vào danh sách cho phép để hỗ trợ dùng bộ cấp phát bộ nhớ của Fil-C
    • Cho phép lệnh gọi sched_yield, được dùng trong cơ chế khóa của Fil-C
  • Lệnh gọi futex dùng cho đồng bộ hóa của Fil-C đã được cho phép sẵn nên không cần thay đổi thêm

Cách Fil-C triển khai prctl

  • Khi cài đặt bộ lọc seccomp, OpenSSH sử dụng hai lệnh gọi prctl
    • PR_SET_NO_NEW_PRIVS để chặn việc có thêm đặc quyền
    • PR_SET_SECCOMP, SECCOMP_MODE_FILTER để kích hoạt bộ lọc
  • Vấn đề là prctl chỉ áp dụng cho luồng gọi nó, nên các luồng runtime khác của Fil-C có nguy cơ vẫn tồn tại mà không có bộ lọc
  • Để ngăn điều đó, Fil-C dùng API filc_runtime_threads_handshake() để áp dụng đồng bộ lên mọi luồng runtime
    • Bảo đảm mỗi luồng đều thực hiện cùng một lệnh gọi prctl
    • Nếu có nhiều luồng người dùng tồn tại, sẽ phát sinh lỗi an toàn Fil-C để tăng cường bảo vệ

Kết luận

  • Sự kết hợp giữa an toàn bộ nhớ và sandboxing là tổ hợp bảo mật mạnh nhất
  • Fil-C tích hợp đầy đủ sandbox dựa trên seccomp của OpenSSH mà vẫn duy trì an toàn bộ nhớ mà không làm suy giảm mức bảo vệ
  • Trong môi trường Linux, việc sử dụng Fil-C cho phép đồng thời đạt được bảo mật cấp hệ thống và an toàn ở cấp độ ngôn ngữ

1 bình luận

 
GN⁺ 2025-12-14
Ý kiến trên Hacker News
  • Thắc mắc vì sao không có nhắc đến landlock

  • Có một cách tiếp cận biên dịch lai theo hướng C → WASM → C
    Làm như vậy có thể kiểm soát hoàn toàn tương tác với OS, đồng thời sandbox truy cập bộ nhớ như WASM nhưng về mặt kỹ thuật vẫn giữ dưới dạng mã C
    Có thể xem tài liệu liên quan tại RLBox

    • Sandbox WASM không đảm bảo tính đúng đắn (soundness) của chương trình
      Nó có thể làm hỏng bộ nhớ, chỉ là phạm vi bị giới hạn trong sandbox mà thôi
      Những hệ thống như SECCOMP đòi hỏi phải định nghĩa tỉ mỉ mọi chính sách tương tác nên khá quan liêu
      Trong khi đó Fil-C chọn cách tiếp cận mà chính ngôn ngữ và runtime cố gắng đảm bảo hành vi đúng đắn của chương trình
      Binary Fil-C là file thực thi thông thường nên cũng có thể dùng cùng các sandbox như SECCOMP
      Linux mất 20 năm để tạo ra giao diện prctl, nên có lẽ phải chờ 10 năm nữa mới thấy thứ tương tự trong WASI
    • RLBox là công nghệ sandbox chứ không phải công nghệ an toàn bộ nhớ
      Ngay cả bên trong sandbox vẫn có thể tạo ra luồng thực thi bất thường
  • Tác giả của Fil-C rất giỏi tạo ra những phát minh thú vị về mặt kỹ thuật, nhưng vẫn lo không biết việc kiểm chứng triển khai đã đủ chưa
    Có nói rằng có thể biên dịch chương trình setuid bằng Fil-C, nhưng phần chỉnh sửa ld.so có thể nguy hiểm
    Ứng dụng setuid phải được viết cực kỳ phòng thủ trước biến môi trường, file descriptor, rlimit, signal v.v.
    Những phần này vẫn còn chưa hoàn thiện nên dùng trong hạ tầng thực tế sẽ có rủi ro
    Dù vậy, thử nghiệm codebase với Fil-C có thể giúp phát hiện những lỗi thú vị

    • Đang thử nghiệm xem có cách nào thực sự phá vỡ Fil-C hay không
    • Nếu thật sự lo ngại thì nên tự thử nghiệm rồi chia sẻ kết quả
    • Mục tiêu là làm cho runtime minh bạch để có thể kiểm toán (auditing)
      Việc sửa ld.so chỉ là thay đổi nhỏ ở mức dạy cho nó về layout của libc
      Lỗi getenv liên quan đến setuid cũng đã được sửa bằng secure_getenv
      Trong những chỉ trích đó có phần đúng và cũng có phần FUD
    • Điều đáng tiếc là tác giả Fil-C phản ứng với chỉ trích một cách thiếu hợp tác
      Trong tình huống data race, pointer và capability trong Fil-C có thể không khớp nhau
      Điều này có thể dẫn đến vi phạm an toàn bộ nhớ
      Tác giả phủ nhận điều này, nhưng việc so sánh với Java là không phù hợp
      Công nghệ thì tuyệt vời, nhưng thái độ của tác giả làm giảm niềm tin
  • WASM vừa là sandbox vừa là môi trường thực thi, và tùy cách sử dụng mà có thể đạt được mức độ an toàn bộ nhớ nhất định
    Khi biên dịch C sang WASM thì bug vẫn còn đó, nhưng phạm vi ảnh hưởng bị giới hạn
    Vì vậy xếp WASM vào nhóm công nghệ sandbox là đúng, nhưng với vai trò môi trường thực thi thì nó còn nhiều khả năng hơn

    • Rốt cuộc WASM vẫn là công nghệ sandbox
      Bug trong mô-đun B có thể đọc được dữ liệu của mô-đun A
      Nghĩa là WASM chỉ là một dạng thay thế cho sandbox tiến trình nhẹ
    • Chính câu “tùy cách sử dụng” đã là bằng chứng cho thấy WASM không hoàn toàn an toàn
      Vì cũng có thể nói C “an toàn tùy cách dùng”
    • WASM không hiểu mô hình bộ nhớ của C, nên không thể triển khai cơ chế bảo vệ như Fil-C
      WASM ngăn việc thoát khỏi runtime, nhưng không ngăn được việc thoát bộ nhớ của chương trình bên trong
  • Có người đề nghị so sánh Fil-C với Rust

    • Cả hai đều rất tốt nhưng cách tiếp cận khác nhau
      Fil-C phù hợp để tăng cường các chương trình C hiện có theo hướng tương thích và bảo mật
      Rust phù hợp để xây dựng codebase mới với an toàn tĩnhhiệu năng
      Fil-C có tổn thất hiệu năng nhất định nhưng hữu ích cho mã C hiện có như ffmpeg, nginx, sudo v.v.
      Rust mạnh ở multithreading và hệ thống kiểu
    • Fil-C đưa GC vào nên có thể bị giảm hiệu năng
      Mục tiêu là đảm bảo an toàn bộ nhớ hơn là cải thiện thiết kế ngôn ngữ
      Nhóm cạnh tranh của nó gần với D, Nim, Go hơn là Rust
    • Fil-C dùng kiểm tra runtime để phát hiện truy cập bộ nhớ không an toàn và dừng chương trình
      Rust thì ngăn chặn ở thời điểm biên dịch
      Hai cách tiếp cận này là trực giao, và Rust cũng có thể bổ sung kiểu kiểm tra runtime theo phong cách Fil-C
  • MicroVM đang ngày càng phổ biến
    Thắc mắc Fil-C có thể tận dụng điều này như thế nào

    • Có lẽ sẽ cần port một chút, nhưng cũng có thể đưa một phần tính năng của microVM lên userland an toàn bộ nhớ của Fil-C
  • Mong dự án này được chú ý hơn
    Sẽ rất tốt nếu những công cụ cốt lõi như sudo hay polkit được phân phối theo cách an toàn bộ nhớ

  • Mong rằng ngay cả trong các ngôn ngữ an toàn bộ nhớ cũng sẽ có nhiều ứng dụng sandboxing hơn
    Đáng tiếc là ngay cả Rust hay Go cũng ít dùng sandbox ở cấp độ OS

    • Seccomp có tính di động và khả năng kết hợp kém
      Khó cấu hình ở cấp thư viện, lại nhạy với phiên bản kernel hay cách triển khai libc
      Nó cũng không thể lọc đầu vào nằm sau pointer như đường dẫn file nên có giới hạn
      Vì vậy thường phải cấu hình trực tiếp ở cấp ứng dụng
    • Rust hầu như không có runtime nên phù hợp với sandboxing
      Trong khi đó Go có runtime lớn nên khó làm an toàn kiểu Fil-C
  • Có người hỏi Fil-C khác gì với Address Sanitizer (ASan) của clang
    Nếu chỉ ở mức tạo runtime panic thì có khó gọi là “an toàn bộ nhớ” hay không

    • ASan bắt bug rất tốt nhưng không đảm bảo an toàn bộ nhớ hoàn chỉnh
      Một số bug vẫn không bị phát hiện
      Nó dùng cách đặt “red zone” quanh bộ nhớ nên chỉ phát hiện được nếu may mắn
    • Nếu lỗi bộ nhớ bị chặn bằng panic trước khi gây ảnh hưởng, thì điều đó cũng có thể được xem là an toàn bộ nhớ
      An toàn bộ nhớ không có nghĩa là “không crash” mà là “truy cập sai không thể tạo ra hiệu ứng”
  • Có người hỏi vì sao không dùng một VM hoàn chỉnh làm sandbox

    • VM là sandbox rất tốt, nhưng các ứng dụng như Chrome hay OpenSSH cần phân tách đặc quyền
      Một tiến trình sẽ parse đầu vào mà không có quyền, tiến trình khác chạy với quyền cao hơn
      Hai tiến trình giao tiếp với nhau bằng IPC
      Dùng VM thì bảo mật cao hơn nhưng overhead lớn, và các chức năng như GPU hay truy cập file trở nên phức tạp
      Vì vậy thông thường sandbox ở cấp OS vẫn gọn gàng hơn
    • VM giải quyết được phần lớn vấn đề, nhưng tăng tốc đồ họa desktop vẫn rất khó
      Phải gán riêng GPU, và ngay cả Qubes cũng chỉ kết nối bằng X11 forwarding nên không có tăng tốc