- Dependency cooldown là một biện pháp bảo mật đơn giản nhưng hiệu quả có thể giảm thiểu phần lớn các cuộc tấn công chuỗi cung ứng mã nguồn mở
- Kẻ tấn công thường chiếm quyền các dự án mã nguồn mở phổ biến để phát tán mã độc, nhưng phần lớn các cuộc tấn công có thời gian lộ diện dưới một tuần
- Nếu thiết lập cooldown để chờ một khoảng thời gian nhất định sau khi phát hành phiên bản mới (ví dụ: 7 ngày), có thể giảm mạnh rủi ro nhiễm mã độc do cập nhật tự động
- Dependabot, Renovate, pnpm v.v. đã hỗ trợ sẵn tính năng cooldown và việc cấu hình rất dễ, không phát sinh chi phí
- Nếu cooldown được cung cấp mặc định ở cấp trình quản lý gói, nó có thể giúp tăng cường bảo mật chuỗi cung ứng và giảm các cảnh báo không cần thiết
Cấu trúc và vấn đề của các cuộc tấn công chuỗi cung ứng
- Phần lớn tấn công chuỗi cung ứng (supply chain attack) có cùng một mẫu hình
- Kẻ tấn công lợi dụng đánh cắp thông tin xác thực của dự án mã nguồn mở phổ biến hoặc lỗ hổng CI/CD để truy cập
- Tải các thay đổi độc hại lên kênh phân phối như PyPI, npm v.v.
- Người dùng cài phải phiên bản bị nhiễm do cập nhật tự động hoặc không ghim phiên bản đầy đủ
- Nhà cung cấp bảo mật phát hiện, đưa ra cảnh báo, sau đó kho gói xóa phiên bản đó
- Khoảng cách giữa bước (1)~(2) có thể dài, nhưng từ (2)~(5) thường được xử lý trong vài giờ đến vài ngày, nên thời gian hoạt động của kẻ tấn công khá ngắn
- Khoảng thời gian có thể bị khai thác (window of opportunity) của các trường hợp lớn trong 18 tháng gần đây
- xz-utils: khoảng 5 tuần
- Ultralytics: 12 giờ (giai đoạn 1), 1 giờ (giai đoạn 2)
- tj-actions: 3 ngày
- chalk: dưới 12 giờ
- Nx: 4 giờ
- rspack: 1 giờ
- num2words: dưới 12 giờ
- Kong Ingress Controller: khoảng 10 ngày
- web3.js: 5 giờ
- Trong số này, 8 vụ có thời gian tấn công dưới 1 tuần, và phần lớn có thể bị chặn bằng cooldown
Khái niệm và hiệu quả của cooldown
- Cooldown là cách trì hoãn việc sử dụng dependency mới trong một khoảng thời gian nhất định sau khi nó được phát hành
- Trong khoảng thời gian này, các nhà cung cấp bảo mật có thể phát hiện xem nó có độc hại hay không
- Ưu điểm
Kết luận và đề xuất
- 8 trên 10 cuộc tấn công có thời gian dưới 1 tuần, và cooldown 7 ngày có thể chặn phần lớn
- Nếu áp dụng cooldown 14 ngày, có thể phòng thủ trước mọi trường hợp ngoại trừ xz-utils
- Cooldown không phải lời giải hoàn hảo, nhưng là cách đơn giản để giảm 80~90% rủi ro phơi nhiễm
- Ngoài Dependabot và Renovate, cũng cần cải thiện để chính các trình quản lý gói hỗ trợ cooldown mặc định
- Bảo mật chuỗi cung ứng không chỉ là vấn đề kỹ thuật mà còn là vấn đề của cấu trúc niềm tin xã hội, nhưng cooldown vẫn là một biện pháp giảm thiểu thực tế hữu ích
3 bình luận
Thực ra nếu không có vấn đề gì thì có lẽ tốt hơn là không cần cập nhật.
Liệu có nhất thiết phải áp dụng một phiên bản mới không khác mấy so với phiên bản trước, trong khi vẫn phải chấp nhận rủi ro hay không.
Ý kiến trên Hacker News
Mọi người lo rằng nếu không cập nhật ngay thì sẽ phơi bày trước lỗ hổng nghiêm trọng, nhưng thực tế đa phần không phải vậy
Nhiều phần mềm không triển khai liên tục mà theo cách khách hàng tự cài phiên bản mới, nên thường cập nhật theo chu kỳ vài tuần hoặc vài tháng
Điều quan trọng là giám sát dependency và kiểm tra các lỗ hổng đã được công bố. Hãy đánh giá xem sản phẩm có thực sự bị ảnh hưởng hay không, rồi chỉ khi đó mới cập nhật ngay dependency đó
Có nhận thức lan rộng rằng cứ có phiên bản mới là phải cập nhật ngay trong ngày
Cách tiếp cận kiểu “để sau sẽ còn vất vả hơn nên làm luôn bây giờ” mà không xem xét thay đổi thực tế là kém hiệu quả
Việc luôn bám sát đầu sóng ngọn gió của số phiên bản đôi khi còn phản tác dụng về mặt bảo mật
Ở công ty chúng tôi, nếu scanner phát hiện lỗ hổng nghiêm trọng thì phải cập nhật trong vòng 7 ngày
Nếu quá hạn sẽ bị coi là vi phạm quy định và kéo theo quy trình phức tạp, nên phần lớn mọi người cứ cập nhật ngay tất cả mọi thứ
Ứng dụng như trình duyệt, nơi có rất nhiều đầu vào bên ngoài, thì cần cập nhật thường xuyên; còn trường hợp như ứng dụng thời tiết với đầu vào hạn chế thì tương đối an toàn hơn
Thà cập nhật định kỳ và đồng thời áp dụng biện pháp phòng vệ trước tấn công chuỗi cung ứng còn hiệu quả hơn
Mô hình để bản phân phối quản lý dependency dùng chung và nâng cấp toàn bộ vài năm một lần, như mô hình Debian stable, ngày càng có vẻ hợp lý hơn
Một số hệ sinh thái di chuyển quá nhanh hoặc có hệ thống đóng gói theo từng bản phân phối còn yếu
Ví dụ, việc cài thư viện Node.js bằng apt rồi dùng trong dự án vẫn còn khó khăn
Một hệ sinh thái di chuyển nhanh mà không có thay đổi nền tảng thì không khỏe mạnh
JS gần như không có tiến bộ thực chất nào trong 3 năm qua, vậy mà để build lại một dự án 3 năm tuổi thì phải chịu nỗi đau gần như viết lại từ đầu
Những bản phân phối như Arch thậm chí có khi không có luôn
Có giả định rằng để ngăn chặn tấn công chuỗi cung ứng, nên đặt thời gian cooldown cho việc cập nhật dependency thay vì lập tức dùng bản mới nhất để chặn 0-day
Đây là sự so sánh giữa xác suất bản cập nhật đưa vào lỗ hổng mới và xác suất nó sửa được lỗ hổng hiện có
Theo SemVer, bản vá patch tương đối an toàn nên cũng có thể áp dụng cách tiếp cận cooldown ngắn hơn cho loại này
Ví dụ, khi đang ở 2.3.4 mà 2.4.0 được phát hành, nếu không có tính năng gấp thì nên đợi tới 2.4.1 rồi hãy nâng cấp
Phần lớn lỗ hổng không đến từ tấn công có chủ đích mà từ bug thông thường
Chính sách giới hạn số lượng và độ phức tạp của dependency là cách tiếp cận mạnh hơn
Thay vì thêm các thư viện “làm mọi thứ”, chỉ nên thêm những dependency nhỏ và có mục đích rõ ràng
Ngoài ra, nếu các thư viện cũng cung cấp phiên bản LTS chỉ bao gồm bản vá bảo mật thì việc quản lý sẽ dễ hơn
Tự triển khai lại có thể là lãng phí. Tôi tò mò ví dụ cụ thể nào về “everything library” là có vấn đề
Nếu bạn tin cùng một lập trình viên qua nhiều thư viện, thì quan hệ tin cậy quan trọng hơn số lượng package
Độ phức tạp có tương quan với lỗ hổng, nhưng không phải nguyên nhân trực tiếp
Giờ đây có thể đưa ra lựa chọn cân nhắc bảo trì dài hạn thay vì chỉ ưu tiên tốc độ ngắn hạn
Trong thế giới C++, đây thậm chí còn là một điểm cạnh tranh
Áp lực phải luôn cập nhật lên bản mới nhất mỗi lần xuất phát từ niềm tin sai lầm rằng phần mềm lúc nào cũng tốt hơn
Thực tế có thể chỉ là đổi những bug đã biết lấy những bug mới chưa biết
Theo dõi các issue đã công khai và chỉ vá khi cần là hợp lý hơn
Tức là có trường hợp phiên bản cũ hơn lại an toàn hơn
Có lẽ cũng vì chúng ta đang dùng phần mềm vốn đã đủ ổn định rồi
Nếu ai cũng nói “đợi thêm một chút”, cuối cùng sẽ thành bi kịch tài nguyên chung và không ai xác minh trước cả
Chờ càng lâu thì nợ kỹ thuật càng tích tụ, nên cần cập nhật dần dần và các biện pháp giảm thiểu như zero trust và monitoring
Trong khoảng thời gian đó các security scanner đã phát hiện lỗ hổng rồi
Nếu kẻ tấn công đẩy lên một bản phát hành trái phép thì đôi khi sẽ bị phát hiện ngay
Ý tưởng cooldown hay, nhưng có rủi ro kẻ tấn công lợi dụng nó để tạo ra cảm giác khẩn cấp giả
Chúng có thể gắn nhãn “bản vá bảo mật khẩn cấp” để dụ cài sớm, trong khi phiên bản đó thực ra là độc hại
Cần đề phòng kiểu tấn công gây áp lực tâm lý này
Một lý do khác cho cooldown là để maintainer có thời gian tự nhận ra việc bị xâm phạm
Kẻ tấn công nhắm vào lúc maintainer vắng mặt như kỳ nghỉ, hội nghị, ngày lễ...
Nhiều dự án chỉ do một hoặc hai người quản lý, nên khoảng chênh thời gian như vậy cực kỳ quan trọng
Điều này phụ thuộc vào đặc tính dự án và bề mặt tấn công
Đã đến lúc phải chấm dứt thời đại chỉ đơn giản làm theo “best practice”
Bảo mật giống như môn thể thao va chạm trực diện, nơi bạn phải suy nghĩ phản biện về chi tiết mỗi ngày
Cooldown cũng có giới hạn nếu chỉ đơn giản cho rằng cứ qua thời gian là sẽ an toàn hơn
Nếu không ai xem mã thì sau một tuần rủi ro vẫn y nguyên
Thay vào đó, cách triển khai dần dần (gradual rollout) có thể hiệu quả hơn
Mỗi bên sử dụng có thể đặt một delay factor, để phía chấp nhận rủi ro cao gặp vấn đề trước,
trong khi những bên còn lại được bảo vệ trong thời gian đó
Các bản cập nhật nguy hiểm sẽ bị loại khỏi hàng đợi, nên những bên trì hoãn sẽ không nhìn thấy chúng nữa
Gần đây đôi khi tôi thấy khó phân biệt xem việc tự làm lại bánh xe hay việc quản lý trò Tetris phụ thuộc thì cái nào còn dễ gánh hơn.
Nếu có gì sai trong một thời gian thì chỉ cần sửa code của mình là được, nhưng trong trò Tetris phụ thuộc, khi không biết bánh xe nào bỗng nhiên lệch đi thì việc debug cũng rất khó.