- GitHub Actions có cấu trúc khai báo và thực thi phụ thuộc gói thông qua cú pháp
uses: trong tệp workflow, về thực chất đang đóng vai trò như một trình quản lý gói
- Tuy nhiên, nó hoàn toàn thiếu các tính năng mà những trình quản lý gói khác cung cấp mặc định như lockfile, hash toàn vẹn, cố định phụ thuộc bắc cầu, khả năng hiển thị cây phụ thuộc
- Theo kết quả nghiên cứu, phần lớn người dùng GitHub Actions đang chạy mã bên ngoài chưa được xác minh, và các lỗ hổng chèn mã đã được phát hiện trong hàng nghìn workflow
- Dù GitHub đã đưa vào một số biện pháp giảm thiểu (bản phát hành bất biến, chính sách cố định SHA, v.v.), các vấn đề về phụ thuộc bắc cầu và tính tái lập vẫn chưa được giải quyết
- Những khiếm khuyết mang tính cấu trúc này ảnh hưởng đến bảo mật chuỗi cung ứng phần mềm nói chung, và cùng một vấn đề cũng lan sang các hệ thống CI khác dựa trên GitHub Actions
Cấu trúc quản lý gói của GitHub Actions và các vấn đề
- Cú pháp như
uses: actions/checkout@v4 là khai báo phụ thuộc, và GitHub sẽ diễn giải, tải xuống rồi thực thi nó
- Điều này giống hệt cách hoạt động của một trình quản lý gói thông thường, nhưng vì không có lockfile nên mỗi lần chạy có thể chọn một phiên bản khác nhau
- So với các trình quản lý gói khác (npm, Cargo, NuGet, v.v.), Actions thiếu toàn bộ lockfile, cố định phụ thuộc bắc cầu, xác minh toàn vẹn, khả năng hiển thị cây phụ thuộc, đặc tả phân giải
- Việc thiếu lockfile là vấn đề cốt lõi, vì quá trình phân giải phụ thuộc có thể thay đổi ở mỗi lần chạy, gây ra bản dựng không xác định
Kết quả nghiên cứu bảo mật và lỗ hổng
- Theo nghiên cứu USENIX Security 2022, 99,7% repository chạy Actions của nhà phát triển bên ngoài, 97% dùng tác giả chưa được xác minh, và 18% đang thiếu các bản cập nhật bảo mật
- Nghiên cứu tiếp theo phát hiện hơn 4.300 lỗ hổng chèn mã trong số 2,7 triệu workflow
- GitHub Actions không cung cấp đầy đủ các thuộc tính bảo mật thiết yếu cho CI/CD như admittance control, execution control, code control, kiểm soát truy cập secrets
Các khiếm khuyết kỹ thuật chính
- Vấn đề phiên bản có thể thay đổi: các tag như
@v4 có thể bị maintainer gắn lại sang commit mới, khiến mã thay đổi âm thầm
- Nếu có lockfile, có thể ghi lại tag đó đã được phân giải thành SHA nào
- Tính mờ đục của phụ thuộc bắc cầu: các Action khác được gọi bên trong Composite Action không hiển thị và không thể kiểm soát
- Theo nghiên cứu, 54% JavaScript Actions chứa điểm yếu bảo mật, và phần lớn phát sinh từ phụ thuộc gián tiếp
- Trong sự cố
tj-actions/changed-files, một cuộc tấn công rò rỉ bí mật đã xảy ra thông qua việc cập nhật phụ thuộc bắc cầu
- Thiếu xác minh toàn vẹn: npm hay Cargo ghi lại hash để xác minh nội dung tải xuống, còn Actions chỉ dựa vào niềm tin theo SHA
- Không thể tái lập khi chạy lại: GitHub nêu rõ rằng “nếu phiên bản bị force-push thì sẽ lấy phiên bản mới nhất”, nên cùng một workflow vẫn có thể chạy mã khác nhau
- Không nhìn thấy cây phụ thuộc: không có tính năng như
npm ls hay cargo tree của npm/Cargo, nên không có cách nào kiểm tra cấu trúc phụ thuộc tổng thể
- Quy tắc phân giải không công khai: cơ chế phân giải phụ thuộc của Actions không được tài liệu hóa, và trong mã
ActionManager.cs chỉ tồn tại logic tải xuống đệ quy đơn giản
Các giới hạn cấu trúc bổ sung
- Không có registry trung tâm: Actions tồn tại trong các Git repository, nên không có chức năng quét bảo mật, phát hiện mã độc, ngăn typosquatting
- Vấn đề môi trường dùng chung: nhiều Action cùng sửa đổi
$PATH, khiến kết quả thay đổi theo thứ tự thực thi
- Không thể chạy offline: phải tải xuống từ GitHub mỗi lần, nên không thể chạy nếu không có mạng
- Điểm yếu namespace: tên người dùng GitHub chính là namespace, nên dễ bị tấn công chiếm tài khoản hoặc đánh máy sai
- Nếu có lockfile và hash toàn vẹn, có thể phát hiện thay đổi mã bằng việc làm bản dựng thất bại
Bối cảnh thiết kế và tác động lan tỏa
- Runner của Actions ban đầu dựa trên Azure DevOps, được thiết kế với giả định môi trường nội bộ đáng tin cậy
- Khi GitHub mở rộng nó thành marketplace công khai, mô hình tin cậy không được thiết kế lại
- Vì vậy, các tính năng cơ bản như lockfile, xác minh toàn vẹn, cố định phụ thuộc bắc cầu, khả năng hiển thị phụ thuộc đã bị bỏ sót
- Khi tính năng tự động phát hành gói dựa trên OIDC được phổ biến, các lỗ hổng bảo mật của Actions bắt đầu ảnh hưởng tới bảo mật chuỗi cung ứng của toàn bộ registry gói
- GitLab CI đã đưa vào từ khóa
integrity để hỗ trợ xác minh hash, nhưng GitHub đã đóng yêu cầu tương tự với trạng thái “không có kế hoạch”
- Các hệ thống CI tương thích GitHub như Forgejo Actions cũng phải giữ nguyên cấu trúc này, nên khiếm khuyết lan rộng ra toàn bộ hệ sinh thái
Đề xuất cải thiện và tình hình hiện tại
- Cộng đồng đã yêu cầu hỗ trợ lockfile (issue #2195), nhưng GitHub đã đóng vào năm 2022 với lý do “không có kế hoạch”
- Nghiên cứu của Palo Alto chứng minh rằng chỉ cố định SHA là không đủ để bảo vệ phụ thuộc bắc cầu
- Một số nhóm đang bù đắp bằng cập nhật Dependabot, vendor vào repository riêng, trình quét bảo mật zizmor
- Giải pháp căn bản là đưa vào lockfile ghi lại SHA và hash toàn vẹn của mọi Action cùng phụ thuộc bắc cầu
- Chừng nào GitHub chưa chấp nhận điều này, việc bảo đảm độ tin cậy của chuỗi cung ứng CI/CD là không thể
2 bình luận
Chắc chỉ đến khi bị hack thì mới tỉnh ra thôi.
Ý kiến trên Hacker News
Không hẳn muốn bênh GHA (GitHub Actions), nhưng tài liệu có ghi rõ là họ khuyến nghị ghim commit SHA để đảm bảo tính ổn định và bảo mật
Có thể tự triển khai kiểu giống lock file, nhưng vẫn không hoàn chỉnh vì không thể kiểm soát transitive dependency
Cuối cùng sẽ phát sinh gánh nặng theo dõi các bản vá bảo mật và cập nhật hash, có lẽ vì vậy nên kiểu ghim theo hash này không được dùng rộng rãi
Phần lớn người dùng không nhận ra vấn đề, mà ngay cả những người nhận ra cũng hầu như không dùng SHA
Cá nhân tôi thích Actions và cũng đang bảo trì vài cái, nhưng nhìn các kho mã công khai thì 90% dùng
@v1, 9% dùng@v1.2, chỉ 1% dùng commit SHAChỉ cần GitHub đầu tư thêm một chút là đã có thể làm ra một giải pháp lock file
Nó thường phụ thuộc vào một phiên bản Node hoặc phiên bản API nhất định, nên đôi khi tôi còn có trải nghiệm cho thấy dùng @main lại tốt hơn
Nó khiến người ta tưởng rằng mình có được một “phiên bản cố định”, nhưng thực ra không phải vậy
Sau khi gặp sự cố tới hai lần tôi mới nhận ra — либо có lock file, либо không có
GitHub gần như không bảo trì các Actions của chính họ, đến mức ngay cả tính năng cơ bản cũng phải dựa vào fork không chính thức
Cảm giác cả hệ sinh thái đang được duy trì bằng các giải pháp chắp vá tạm bợ
Vì GitHub đã công bố sẽ ưu tiên di chuyển sang Azure hơn là phát triển tính năng
Bài viết liên quan
Công ty nhỏ của chúng tôi cũng phải trả hơn 200 USD mỗi tháng
Có vẻ nó được xem là một nguồn doanh thu mới còn quan trọng hơn cả Windows
Có lẽ các tác giả ban đầu đã rời công ty rồi
Không biết có ai từng thử dùng ArgoCD làm pipeline CI chưa
Tôi nghĩ GHA là một ví dụ thất bại của triết lý 'less is more'
Việc nó trở thành tiêu chuẩn ngành mới là vấn đề lớn nhất
Chỉ cần đầu tư thêm một chút là đã có thể tạo ra một CI tốt hơn nhiều, nhưng cảm giác như MS đang lặp lại sai lầm thời IE6
Giờ thì thế hệ kỹ sư trẻ không có trải nghiệm so sánh nên cũng không nhận ra những giới hạn đó
Tôi đang tính biến một chiếc laptop nghỉ hưu thành server Woodpecker. Muốn xem một hệ CI mà mọi người không ghét sẽ trông như thế nào
So với thế thì GHA có vẻ không mang lại mấy giá trị
Khi công ty định chuyển từ Jenkins/Ansible sang GHA, tôi đã phản đối, và giờ nhìn lại có vẻ đó là quyết định đúng
CI lúc nào cũng có gánh nặng bảo trì rất lớn, đặc biệt môi trường Mac thì vẫn cực kỳ khó xử lý
Với câu hỏi: “Bạn có tin GitHub sẽ cung cấp đúng mã SHA không?”,
nếu nhìn vào thực tế là đa số đều đang dùng GitHub-hosted runner, thì nếu bạn không tin điều đó, có lẽ bạn đã có một vấn đề còn lớn hơn nhiều rồi
Tôi có nghĩ đến chuyện sẽ thế nào nếu GitHub Actions có kiến trúc local-first, hỗ trợ locking dựa trên Nix
cachix/cloud.devenv.sh
Nhiều Actions bên thứ ba trong tài liệu hay ví dụ vẫn tham chiếu trực tiếp tới nhánh master
Chỉ cần một lần push độc hại là có thể gây ra rò rỉ dữ liệu trên vô số kho mã
Dùng tag cũng không phải phòng vệ hoàn toàn vì tag có thể bị di chuyển
Khi nhìn vào bốn thuộc tính bảo mật cốt lõi của CI/CD mà các nhà nghiên cứu nói tới (xác thực, thực thi, mã nguồn, quyền truy cập bí mật), tôi thấy băn khoăn
Liệu CI/CD có thực sự cần truy cập secrets không?
Tôi nghĩ chỉ cần quyền gọi API là đủ
Một hệ thống lý tưởng không nên trực tiếp xử lý secrets, mà nên xử lý gián tiếp thông qua adapter như secure enclave
Trong thực tế, đa số khách hàng vẫn cần secrets
Nền tảng ít nhất phải cung cấp cơ chế quản lý secrets an toàn
Đặc biệt là các dự án mã nguồn mở muốn triển khai trực tiếp từ CI
Tôi muốn biết cụ thể “cách secure enclave” sẽ khác ra sao
nhưng thực tế mỗi môi trường lại khác nhau và chi phí triển khai rất cao, nên đa số chốt ở cách container + biến môi trường
Muốn tự động hóa các bài test này thì secrets là điều không thể tránh khỏi
Nếu phải commit lock file vào kho mã thì sẽ phát sinh vấn đề bootstrapping ở thời điểm tạo lần đầu
Muốn giải quyết việc đó thì cần có khả năng chạy Actions ở local
Có công cụ như nektos/act, nhưng nó chủ yếu để debug
Có lẽ sẽ cần một cơ chế riêng dựa trên phân tích tĩnh