Vì sao 70% lỗ hổng nghiêm trọng đến từ lỗi bộ nhớ trong C/C++
Tóm tắt
Microsoft và Chromium đều thấy khoảng 70% lỗ hổng nghiêm trọng là lỗi an toàn bộ nhớ, lớp lỗi gần như chỉ có ở C/C++. Sandbox, fuzzing hay kỷ luật code giúp giảm nhưng không loại bỏ được, nên lời giải bền vững là loại lớp lỗi từ ngôn ngữ.
Heartbleed năm 2014 cho phép kẻ tấn công đọc trộm bộ nhớ của hàng loạt máy chủ dùng OpenSSL, lấy cả khoá bí mật lẫn mật khẩu người dùng. Nguyên nhân không phải thuật toán mã hoá yếu, mà là một phép đọc vượt biên bộ đệm trong code C. Đây không phải ngoại lệ. Microsoft và đội Chromium đều công bố con số gần như trùng nhau: khoảng 70% lỗ hổng nghiêm trọng của họ là lỗi an toàn bộ nhớ, một loại lỗi gần như chỉ tồn tại ở ngôn ngữ như C và C++.
Con số từ những đội lớn nhất
| Nguồn | Phát hiện |
|---|---|
| Microsoft (MSRC, 2019) | Khoảng 70% lỗ hổng được cấp CVE mỗi năm là lỗi an toàn bộ nhớ |
| Chromium | Khoảng 70% lỗi bảo mật mức cao là lỗi bộ nhớ, trong đó một nửa là use-after-free (phân tích 912 lỗi cao/nghiêm trọng từ 2015) |
| Android (Google, 2019) | 76% lỗ hổng là lỗi an toàn bộ nhớ |
| Nhà Trắng (ONCD, 2024) | Chỉ ra lỗi bộ nhớ là gốc rễ chung của Morris worm, Slammer, Heartbleed, Trident, Blastpass |
Các kiểu lỗi bộ nhớ phổ biến
| Lỗi | Chuyện gì xảy ra | Hậu quả |
|---|---|---|
| Buffer overflow | Ghi vượt quá vùng nhớ được cấp | Ghi đè dữ liệu, chiếm quyền thực thi code |
| Out-of-bounds read | Đọc vượt biên (như Heartbleed) | Lộ khoá, mật khẩu, dữ liệu người khác |
| Use-after-free | Dùng con trỏ tới vùng nhớ đã giải phóng | Kẻ tấn công đặt dữ liệu giả vào chỗ trống đó |
| Double free | Giải phóng một vùng nhớ hai lần | Hỏng cấu trúc quản lý bộ nhớ, dễ bị khai thác |
| Biến chưa khởi tạo | Đọc giá trị rác còn sót lại | Rò rỉ thông tin, hành vi khó lường |
| Data race | Nhiều luồng ghi cùng dữ liệu không đồng bộ | Hỏng dữ liệu, mở đường cho các lỗi trên |
char *buf = malloc(64);
free(buf);
/* ... nhiều dòng sau, ở hàm khác ... */
strcpy(buf, user_input); /* use-after-free + không kiểm tra độ dài */
Trình biên dịch C chấp nhận đoạn code này. Nó có thể chạy "bình thường" hàng năm trời cho tới khi ai đó gửi đúng input.
Vì sao "code cẩn thận hơn" không đủ?
- Quy mô: codebase hàng chục triệu dòng, hàng nghìn người sửa; chỉ cần một chỗ sai.
- Lỗi trải đều: Chromium ghi nhận lỗi bộ nhớ rải đều khắp codebase, không tập trung ở vài file "nguy hiểm".
- Biện pháp giảm nhẹ có giới hạn: sandbox, ASLR, fuzzing, sanitizer làm khai thác khó hơn nhưng không loại bỏ lớp lỗi. Chromium cho biết sandbox và site isolation đã chạm giới hạn vì mỗi tiến trình tốn tài nguyên, nhất là trên Android.
- Kết luận của DARPA: sau hơn hai thập kỷ, cộng đồng kỹ thuật đi tới nhận định công cụ tìm lỗi thôi là không đủ; cần ngôn ngữ từ chối chương trình không an toàn ngay lúc biên dịch.
Lưu ý để không hiểu sai
- C/C++ không "xấu": chúng vẫn là nền của hệ điều hành, trình duyệt, game, nhúng. Vấn đề là kiểu lỗi này rất khó tránh ở quy mô lớn chỉ bằng kỷ luật.
- 30% còn lại vẫn tồn tại: lỗi logic, phân quyền, injection không biến mất khi đổi ngôn ngữ.
- C++ hiện đại giúp nhưng không đảm bảo: smart pointer, container có kiểm tra giảm rủi ro, nhưng trình biên dịch không bắt buộc bạn dùng chúng đúng.
Áp dụng thế nào?
- Dev: Bật AddressSanitizer và fuzzing cho code C/C++ xử lý input bên ngoài; ưu tiên container có kiểm tra biên.
- Tech Lead: Thống kê CVE và bug bảo mật của chính sản phẩm theo loại; nếu lỗi bộ nhớ chiếm phần lớn, đó là dữ liệu để đề xuất ngôn ngữ an toàn bộ nhớ.
- PM: Xếp các thành phần parse dữ liệu không tin cậy (file, mạng, ảnh, video) vào nhóm rủi ro cao nhất khi lập kế hoạch bảo mật.
Nguồn & tham khảo
- A proactive approach to more secure code — Microsoft Security Response Center
- Memory safety — The Chromium Projects
- Back to the Building Blocks: A Path Toward Secure and Measurable Software — White House ONCD
- The Heartbleed Bug — heartbleed.com
- TRACTOR: Translating All C to Rust — DARPA