
Ghim dependency bằng SHA: ba vụ gần đây và cách chặn
LiteLLM, Tensorlake và TanStack đều lan qua floating version. Vì sao ghim version chính xác vẫn chưa đủ, và ba bước chuyển sang ghim bằng hash.
CI của bạn có dòng uses: actions/checkout@v4, và package.json có "some-sdk": "^0.5.0". Cả hai nói cùng một điều: cho tôi bản mới nhất còn chấp nhận được, ngay lúc này.
Nghe như một thói quen tốt, vì bạn nhận patch tự động. Nhưng như tác giả Giorgio Boa viết, nó cũng có nghĩa là "the code you run today is not the code you ran yesterday, and nobody reviewed the difference".
Ba vụ gần đây, cùng một cơ chế
Bài viết dẫn ba sự cố cụ thể:
- LiteLLM, 21/10. Một bản release bị chèn mã độc được đẩy lên PyPI và Docker Hub, "live for about two hours". Ai chạy
pip install litellmkhông kèm version, hoặc để auto-updater bám theolatest, nhận một credential stealer "that ran on every Python start on the machine". - Tensorlake npm SDK, 08/10. Version 0.5.144 mang một biến thể mới của worm Shai-Hulud. Một khai báo
^0.5.0"would happily resolve to it". Worm lấy token rồi republish các package khác của nạn nhân. - TanStack, đầu năm nay. "84 malicious versions of 42 packages, all with valid provenance." Một project phía dưới merge một bản Dependabot bump thường lệ "24 minutes after it opened", và worm dùng token đó publish thêm 110 bản nữa.
Kết luận của tác giả gọn: "Pinning doesn't make malicious code safe, but floating versions let malicious code reach you automatically, immediately and silently."
Ghim version chính xác vẫn chưa đủ
Đây là phần nhiều người làm nửa vời. Một Git tag như v4.2.2 "is exactly as movable as v4" — ai kiểm soát repo đều trỏ nó sang commit khác được. Docker tag cũng mutable: trong vụ LiteLLM, "the real v1.107.0 tag pointed at the attacker's image". Trên PyPI, maintainer còn thêm file mới vào một release đã tồn tại được.
Trường hợp GitHub Actions có tiền lệ rõ: sự cố tj-actions/changed-files năm 2025 (CVE-2025-30066), kẻ tấn công dời tag sẵn có sang commit độc, và mọi workflow tham chiếu tag đó "started dumping its CI secrets into public build logs".
Thứ duy nhất không dời được là hash của nội dung: commit SHA, image digest, package integrity hash.
Ba bước, theo từng hệ sinh thái
1. Ghim bằng hash, không bằng tên. Manifest cứ ghi ý định (^4.2.0), nhưng thứ thực sự cài phải đến từ lockfile có integrity hash cho mọi package, kể cả transitive. Commit lockfile và cài từ nó.
2. Ép kiểm tra hash lúc cài. Hash nằm trong file mà không ai verify thì vô nghĩa. Dùng npm ci, pip install --require-hashes, uv sync --locked, cargo build --locked.
3. Update có chủ đích, có độ trễ. Ghim không đồng nghĩa với không update. Tác giả lập luận rằng "most malicious versions are pulled within hours", nên một khoảng chờ vài ngày lọc được phần lớn.
Cụ thể theo stack:
- GitHub Actions: tham chiếu bằng commit SHA đủ 40 ký tự, giữ tag ở dạng comment. Bài viết cho biết workflow lockfile nay GA trên mọi plan:
gh actions-lockghi.github/workflows/actions.lockvới commit chính xác của mọi action, và admin ép được bằng policy Require lockfile. - npm: commit lockfile và dùng
npm ci, không dùngnpm installtrong CI. Thêmignore-scripts=truevào.npmrc, vì install-time script chính là đường chạy của Shai-Hulud. - Python:
uv lock+uv sync --locked, hoặcpip-compile --generate-hashesrồi cài bằng--require-hashes. - Container: ghim theo digest
@sha256:, không theo tag, và đừng để auto-updater kéo thẳng image production từ registry public.
Việc làm được ngay hôm nay: mở một workflow bất kỳ, đổi một action từ tag sang SHA 40 ký tự, và bật npm ci trong CI. Hai thay đổi đó mất vài phút và cắt đúng con đường mà cả ba vụ trên đã đi.
Không spam, hủy đăng ký bất kỳ lúc nào.
Bài viết liên quan


AnySpeechRecognizer: đổi engine ASR on-device trên macOS 26
30 thg 10, 2026