Tại Sao GitHub Actions Lại Trở Thành Mục Tiêu Tấn Công Phổ Biến
Trong vài năm gần đây, GitHub Actions script injection vulnerability đã nổi lên như một trong những mối đe dọa nghiêm trọng nhất đối với các dự án phần mềm hiện đại. Nguyên nhân không phải vì GitHub Actions kém bảo mật, mà vì hệ thống này nằm ngay tại trung tâm của mọi quy trình phát triển phần mềm — nơi code được kiểm tra, build được tạo ra, và credential được sử dụng liên tục.
GitHub Actions cho phép các nhóm phát triển tự động hóa gần như toàn bộ vòng đời của phần mềm. Chính sự tiện lợi đó tạo ra bề mặt tấn công rộng lớn. Mỗi workflow file là một kịch bản thực thi lệnh trên máy chủ thật, với quyền truy cập vào secrets, token xác thực, và đôi khi cả hệ thống nội bộ của tổ chức. Khi kẻ tấn công tìm được cách can thiệp vào luồng xử lý đó — dù chỉ một dòng lệnh — hậu quả có thể lan rộng ra toàn bộ hạ tầng.
Điều đáng lo ngại hơn là các CI/CD pipeline exploit ngày càng được thực hiện bởi các công cụ tự động, không cần kẻ tấn công phải ngồi theo dõi từng bước. Vụ việc liên quan đến Snowflake và Wiz Red Agent vào tháng 6 năm 2026 là minh chứng rõ ràng nhất cho xu hướng này — và đồng thời là ví dụ điển hình về mức độ nguy hiểm của GitHub Actions script injection vulnerability trong môi trường sản xuất thực tế.
Cơ Chế Hoạt Động Của Lỗ Hổng Chèn Lệnh Trong Workflow Tự Động

Luồng thực thi lệnh khi GitHub Actions xử lý dữ liệu đầu vào
Để hiểu tại sao GitHub Actions script injection vulnerability lại nguy hiểm, cần nắm được cách GitHub Actions xử lý dữ liệu. Khi một workflow được kích hoạt bởi một sự kiện — chẳng hạn ai đó mở một Issue — GitHub Actions sẽ thu thập thông tin từ sự kiện đó và đưa vào ngữ cảnh thực thi. Các giá trị như tiêu đề issue, nội dung comment, hay tên nhánh đều có thể được truy cập thông qua cú pháp ${{ github.event.issue.title }}.
Vấn đề nằm ở chỗ: cú pháp ${{ }} được GitHub mở rộng (expand) trước khi shell nhận được lệnh để thực thi. Điều đó có nghĩa là nếu một giá trị từ người dùng được nhúng thẳng vào một khối run:, toàn bộ nội dung đó sẽ được truyền vào shell như thể đó là một phần của script gốc.
Điểm yếu chết người: Nội suy biến trực tiếp thay vì biến môi trường an toàn
Có hai cách để đưa dữ liệu từ context vào shell script trong GitHub Actions. Cách thứ nhất — và an toàn — là khai báo giá trị qua khối env:, sau đó tham chiếu đến biến môi trường trong shell. Với cách này, dù người dùng nhập bất cứ thứ gì, nó đều được shell coi là một chuỗi ký tự thông thường, không phải lệnh.
Cách thứ hai — và cực kỳ nguy hiểm — là nội suy trực tiếp: viết ${{ github.event.issue.title }} thẳng vào trong khối run:. Khi đó, GitHub sẽ thay thế biểu thức này bằng nội dung thực của tiêu đề issue trước khi shell nhìn thấy lệnh. Kết quả là shell nhận được một đoạn văn bản tùy ý do người dùng kiểm soát, nằm lẫn trong code thực thi.
Đây chính là trái tim của mọi CI/CD pipeline exploit liên quan đến GitHub Actions: sự nhầm lẫn giữa dữ liệu và lệnh ở tầng xử lý template — và cũng là lý do khiến GitHub Actions script injection vulnerability khó phát hiện đến vậy.
So Sánh Trực Tiếp: Pattern An Toàn Và Pattern Nguy Hiểm
| Tiêu chí | ❌ Pattern nguy hiểm (Nội suy trực tiếp) | ✅ Pattern an toàn (Biến môi trường) |
|---|---|---|
| Cú pháp trong workflow | run: echo "${{ github.event.issue.title }}" |
env: |
| Thứ tự xử lý | GitHub expand template → Shell nhận chuỗi tùy ý từ người dùng | GitHub expand template → Shell nhận tên biến → OS lấy giá trị từ environment |
Rủi ro với ký tự đặc biệt (' ;` $) |
Rất cao — attacker có thể inject và thực thi lệnh shell tùy ý | Không — shell luôn xử lý nội dung biến như dữ liệu chuỗi thuần túy |
| Phát hiện qua static analysis | Dễ bị bỏ sót nếu tool không mô phỏng template expansion | Không có vấn đề cần phát hiện |
| Mức độ kiểm soát của attacker | Toàn quyền inject lệnh shell tùy ý vào runner | Không thể thoát ra ngoài ngữ cảnh dữ liệu |
| Ví dụ thực tế | Pattern bị Copilot Autofix đưa vào repo Snowflake tháng 6/2026 | Pattern an toàn ban đầu của repo Snowflake trước khi bị thay thế |
Vì sao một dấu nháy đơn có thể phá vỡ toàn bộ chuỗi bảo mật
Hãy tưởng tượng workflow có đoạn lệnh sau:
TITLE=$(echo '${{ github.event.issue.title }}')
Nếu tiêu đề issue chứa dấu nháy đơn ('), shell sẽ hiểu đó là ký tự kết thúc chuỗi. Mọi thứ phía sau dấu nháy đó có thể được thực thi như lệnh shell độc lập. Kẻ tấn công chỉ cần đặt tiêu đề issue theo dạng: '; lệnh_độc_hại; echo ' — và lệnh của họ sẽ chạy trong môi trường của GitHub Actions runner, với toàn quyền truy cập vào secrets được cấu hình cho workflow đó. Đây là bản chất cốt lõi của GitHub Actions script injection vulnerability mà mọi developer cần nắm vững.
Khi AI “Sửa Lỗi” Lại Tạo Ra Lỗ Hổng Mới: Bài Học Từ Vụ Snowflake

Copilot Autofix đã thay thế đoạn code an toàn bằng đoạn code nguy hiểm như thế nào
Ngày 18 tháng 6 năm 2026, một pull request mang tên “SNOW-2069227: Update jira workflows” được merge vào repository snowflakedb/snowflake-connector-net. Commit squash cuối cùng ghi nhận đồng tác giả là “Copilot Autofix powered by AI” — đây chính là GitHub Copilot security flaw điển hình nhất được ghi nhận trong năm, và cũng là minh chứng rõ ràng cho hậu quả của GitHub Actions script injection vulnerability khi bị đưa vào môi trường production.
Điều đáng chú ý là trước khi PR này được merge, repository đã có một pattern xử lý an toàn: tiêu đề issue được truyền qua biến môi trường env:, sau đó được xây dựng thành JSON payload thông qua công cụ jq với tham số --arg. Pattern này đã được thiết kế có chủ đích để ngăn chặn script injection.
PR do Copilot Autofix đề xuất đã loại bỏ hoàn toàn cơ chế bảo vệ đó. Thay vào đó, nó dùng cú pháp nội suy trực tiếp ${{ github.event.issue.title }} — đúng cái pattern mà người viết code trước đó đã cố tình tránh. Một công cụ AI, không có ngữ cảnh về lịch sử thiết kế, đã xóa đi một lớp bảo vệ được xây dựng cẩn thận và thay bằng cách viết “ngắn gọn hơn” nhưng chứa lỗ hổng nghiêm trọng.
Tại sao công cụ review bảo mật AI của GitHub không phát hiện được vấn đề
Sau khi PR được tạo ra, GitHub’s AI-assisted security review — một tính năng được thiết kế để phát hiện các vấn đề bảo mật trước khi code được merge — đã không đánh dấu bất kỳ cảnh báo nào. Đây là minh chứng cho một điểm yếu cốt lõi của GitHub Copilot security flaw hiện tại: các công cụ AI review thường phân tích code trong trạng thái tĩnh, không mô phỏng đầy đủ quá trình template expansion xảy ra tại runtime.
Nói cách khác, công cụ review nhìn vào dòng code và thấy một biểu thức hợp lệ về mặt cú pháp. Nhưng nó không đặt câu hỏi: “Giá trị này đến từ đâu? Người dùng bên ngoài có kiểm soát được nó không? Điều gì xảy ra nếu nó chứa ký tự đặc biệt?” — đó là tư duy cần thiết để phát hiện GitHub Actions script injection vulnerability nhưng lại không được triển khai đầy đủ trong quy trình review tự động.
AI Tấn Công Lại AI: Wiz Red Agent Khai Thác Lỗ Hổng Hoàn Toàn Tự Động

Chỉ 5 ngày sau khi lỗ hổng đi vào production, Wiz Red Agent — một công cụ nghiên cứu bảo mật tự động của Wiz Research — đã quét tổ chức GitHub của Snowflake và xác định workflow jira_issue.yml trong repository snowflake-connector-net là mục tiêu tiềm năng cho GitHub Actions script injection vulnerability. Không có sự tham gia của con người trong giai đoạn phát hiện này.
Red Agent phát hiện và điều chỉnh payload sau khi gặp lỗi cú pháp bash
Wiz Red Agent xây dựng payload khai thác và tiến hành thử nghiệm. Lần đầu, nó sử dụng ký tự # để comment out phần còn lại của dòng lệnh — một kỹ thuật injection cơ bản. Tuy nhiên, GitHub Actions runner trả về lỗi bash: ký tự # đã vô tình “ăn” mất dấu đóng ngoặc ) của biểu thức TITLE=$(...), gây ra lỗi EOF không mong đợi.
Đây là lúc khả năng tự học của Red Agent thể hiện rõ: thay vì dừng lại hoặc báo cáo thất bại, hệ thống tự phân tích thông báo lỗi, hiểu ra nguyên nhân cú pháp, và điều chỉnh payload. Phiên bản mới sử dụng ; echo ' để đóng đúng cú pháp shell trước khi chèn lệnh độc hại. Lần này, callback thành công.
Toàn bộ quá trình từ quét mã nguồn đến đánh cắp thông tin chỉ trong vài phút
Trong vòng vài phút sau khi payload được tinh chỉnh, một GitHub Actions runner (địa chỉ IP Azure: 20.106.182.197) đã gửi callback đến listener của Wiz, mang theo thông tin xác thực được mã hóa base64. Token đó thuộc tài khoản qa@snowflake.net và cấp quyền đọc trên hệ thống Jira nội bộ của Snowflake tại snowflakecomputing.atlassian.net.
Toàn bộ chuỗi hoạt động — từ quét repository, nhận diện GitHub Actions script injection vulnerability, tạo payload, xử lý lỗi, điều chỉnh và khai thác thành công — diễn ra hoàn toàn tự động. Đây là hình ảnh rõ nét nhất về tương lai của CI/CD pipeline exploit: không cần hacker, chỉ cần một AI đủ thông minh.
“Cổng Bảo Mật” Trông Có Vẻ Vững Chắc Nhưng Thực Ra Luôn Mở Toang
Điều kiện kiểm tra if: vô hiệu hóa bởi giá trị null trong sự kiện issues
Workflow có một điều kiện if: mà nhìn qua tưởng như là một lớp kiểm soát truy cập:
if: github.event.pull_request.user.login != 'whitesource-for-github-com[bot]'
Điều kiện này có vẻ hợp lý — nó cố gắng loại trừ một bot cụ thể. Nhưng vấn đề nằm ở ngữ cảnh: workflow được kích hoạt bởi sự kiện issues: opened, không phải pull_request. Trong sự kiện issues, github.event.pull_request luôn là null. Do đó, điều kiện thực chất là null != 'whitesource-for-github-com[bot]' — và điều này luôn đúng.
Bất kỳ người dùng GitHub nào cũng có thể kích hoạt workflow mà không cần xác thực
Kết quả là “cổng bảo mật” này không lọc bất kỳ ai. Mọi tài khoản GitHub — dù là contributor, người dùng lạ, hay thậm chí tài khoản mới tạo — đều có thể mở một Issue trên repository public và kích hoạt toàn bộ workflow, bao gồm cả bước thực thi shell có chứa GitHub Actions script injection vulnerability. Không cần xác thực đặc biệt, không cần quyền cộng tác viên, không cần bất cứ điều kiện nào ngoài một tài khoản GitHub hợp lệ.
Thiệt Hại Thực Tế Và Phạm Vi Ảnh Hưởng Khi Thông Tin Jira Bị Lộ
Token bị đánh cắp trong vụ việc này không chỉ là một chuỗi ký tự vô nghĩa. Nó xác thực thành công với hệ thống Atlassian Jira của Snowflake và cấp quyền đọc trên nhiều project quan trọng, bao gồm các project về kỹ thuật nội bộ, tuân thủ bảo mật, và cả hệ thống theo dõi bug bounty.
Điều đó có nghĩa là kẻ tấn công — nếu là người thật với ý định xấu — có thể đọc toàn bộ lịch sử báo cáo lỗ hổng bảo mật chưa được vá, các thông tin về quy trình vận hành nội bộ, và thậm chí tìm hiểu cách Snowflake xử lý các sự cố bảo mật. Đây là loại thông tin có giá trị rất cao cho các cuộc tấn công tiếp theo.
Snowflake đã phản ứng rất nhanh sau khi nhận được báo cáo từ Wiz vào ngày 23 tháng 6 năm 2026: vá lỗi trong ngày, thu hồi và cấp lại token, đồng thời xem xét toàn bộ audit log để xác nhận rằng Wiz là tổ chức duy nhất đã truy cập trong khoảng thời gian 5 ngày lỗ hổng tồn tại. Wiz cũng xác nhận đã xóa toàn bộ dữ liệu thu thập được trong quá trình kiểm tra. Bản vá khôi phục hoàn toàn pattern an toàn ban đầu: sử dụng env: variable kết hợp với jq --arg để xử lý dữ liệu đầu vào.
Những Sai Lầm Phổ Biến Khi Viết GitHub Actions Workflow

Dưới đây là các lỗi thường gặp nhất mà developer mắc phải khi xây dựng CI/CD workflow, dẫn đến nguy cơ bị khai thác GitHub Actions script injection vulnerability tương tự vụ Snowflake:
- Nội suy trực tiếp dữ liệu người dùng vào khối
run:— Dùng${{ github.event.issue.title }},${{ github.event.comment.body }}, hay${{ github.head_ref }}thẳng trong shell script thay vì khai báo quaenv:. - Tin tưởng vào điều kiện
if:mà không kiểm tra ngữ cảnh runtime — Viết điều kiện lọc tham chiếu đến trường không tồn tại trong loại sự kiện đang xử lý (ví dụ: dùnggithub.event.pull_requesttrong workflow kích hoạt bởiissues). - Không giới hạn quyền (
permissions:) ở mức tối thiểu cần thiết — Để workflow chạy với quyền mặc định rộng thay vì khai báo tường minh từng quyền cụ thể, khiến thiệt hại khi bị khai thác mở rộng hơn mức cần thiết. - Merge PR từ AI mà không kiểm tra ngữ cảnh lịch sử thiết kế — Chấp nhận thay đổi từ Copilot Autofix hoặc công cụ AI tương tự mà không xem xét lý do tại sao pattern cũ được viết theo cách đó.
- Không triển khai static analysis tự động cho workflow files — Bỏ qua các công cụ như
zizmorhoặc CodeQL security query dành riêng cho Actions, dẫn đến GitHub Actions script injection vulnerability tồn tại nhiều ngày trước khi bị phát hiện. - Dùng secrets với phạm vi quyền quá rộng trong workflow — Cấp token có quyền đọc/ghi toàn bộ hệ thống cho một workflow chỉ cần một tác vụ nhỏ, biến mọi lỗ hổng injection thành cánh cửa vào toàn bộ hạ tầng.
- Không thiết lập timeout cho workflow steps — Để runner chạy không giới hạn thời gian, tạo điều kiện cho các lệnh độc hại thực thi callback hoặc exfiltrate dữ liệu mà không bị gián đoạn.
Câu Hỏi Thường Gặp Về Lỗ Hổng Script Injection Trong GitHub Actions
GitHub Actions script injection vulnerability có ảnh hưởng đến repository private không?
Có, và đây là điều nhiều người hay nhầm lẫn. GitHub Actions script injection vulnerability không phụ thuộc vào việc repository là public hay private. Điều quan trọng là workflow có được kích hoạt bởi sự kiện nào và dữ liệu đầu vào từ sự kiện đó có được xử lý an toàn không. Trong trường hợp repository private, phạm vi người có thể khai thác có thể hẹp hơn — nhưng nếu workflow cho phép bất kỳ thành viên nào của tổ chức kích hoạt nó, rủi ro vẫn rất cao. Đặc biệt với các workflow dùng secrets hoặc kết nối với hệ thống nội bộ, hậu quả khi bị khai thác trong môi trường private thậm chí còn nghiêm trọng hơn vì người ta thường ít cảnh giác hơn.
Làm thế nào để kiểm tra workflow của mình có đang dùng pattern nguy hiểm hay không?
Cách đơn giản nhất để phát hiện GitHub Actions script injection vulnerability là tìm kiếm trong tất cả file .yml trong thư mục .github/workflows/ của bạn theo pattern ${{ xuất hiện bên trong khối run:. Nếu bạn thấy các biểu thức như ${{ github.event.issue.title }}, ${{ github.event.comment.body }}, hay ${{ github.head_ref }} nằm trực tiếp trong shell script, đó là dấu hiệu cần xem xét ngay. Ngoài ra, công cụ zizmor — một static analyzer dành riêng cho GitHub Actions — có thể tự động phát hiện các pattern này. Bạn cũng có thể dùng CodeQL với query bảo mật có sẵn cho Actions workflows.
Sự khác biệt giữa dùng env: variable và nội suy ${{ }} trực tiếp là gì?
Khi bạn dùng nội suy trực tiếp — ví dụ echo "${{ github.event.issue.title }}" trong khối run: — GitHub mở rộng biểu thức đó thành nội dung thực trước khi shell nhận lệnh. Shell nhìn thấy một chuỗi văn bản tùy ý từ người dùng nằm lẫn trong lệnh, và nếu chuỗi đó chứa ký tự shell đặc biệt, nó sẽ được thực thi. Đây chính là bản chất của GitHub Actions script injection vulnerability. Ngược lại, khi bạn khai báo qua env::
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
run: echo "$ISSUE_TITLE"
…shell nhận được một biến môi trường và tham chiếu đến nó. Dù nội dung biến có chứa dấu nháy, dấu chấm phẩy hay bất kỳ ký tự nào khác, shell vẫn xử lý nó như một chuỗi dữ liệu thông thường, không phải lệnh. Đây là ranh giới cốt lõi giữa dữ liệu và lệnh mà mọi developer làm việc với CI/CD workflow cần nắm vững.
Checklist Bảo Mật Workflow Trước Khi Merge
Sử dụng danh sách kiểm tra này trước khi merge bất kỳ thay đổi nào vào workflow file, đặc biệt khi thay đổi đến từ công cụ AI, để ngăn chặn GitHub Actions script injection vulnerability xâm nhập vào codebase của bạn:
- ☐ Tất cả dữ liệu từ
github.event.*được truyền quaenv:thay vì nội suy trực tiếp vàorun: - ☐ Các điều kiện
if:tham chiếu đúng trường tồn tại trong loại sự kiện kích hoạt workflow - ☐ Khối
permissions:được khai báo tường minh ở mức tối thiểu cần thiết - ☐ Secrets chỉ được cấp phạm vi quyền đúng với tác vụ cụ thể của workflow
- ☐ Công cụ
zizmorhoặc CodeQL đã quét và không có cảnh báo script injection - ☐ Nếu PR đến từ AI, đã so sánh với pattern cũ để xác nhận không có lớp bảo vệ nào bị xóa bỏ
- ☐ Timeout đã được thiết lập cho các step có khả năng thực thi lệnh từ input bên ngoài
Kết Luận: Bảo Mật CI/CD Trong Kỷ Nguyên Cả Người Tấn Công Lẫn Người Phòng Thủ Đều Dùng AI
Vụ việc Snowflake không chỉ là một câu chuyện về một lỗ hổng kỹ thuật đơn lẻ. Nó là bức tranh thu nhỏ của một thực tế mới: các công cụ AI như Wiz Red Agent có thể phát hiện và khai thác GitHub Actions script injection vulnerability nhanh hơn nhiều so với tốc độ con người có thể review code. Trong khi đó, các công cụ AI sinh code như Copilot — dù có tính năng “autofix” — vẫn có thể vô tình xóa bỏ các lớp bảo vệ mà người viết code trước đó đã dày công xây dựng.
Ba bài học cần nhớ: Thứ nhất, AI sinh code không có ký ức về lý do tại sao một đoạn code được viết theo cách cụ thể — đó là công việc của con người phải giữ lại ngữ cảnh đó. Thứ hai, thời gian tồn tại của một GitHub Actions script injection vulnerability không còn tính bằng tuần hay tháng nữa — trong trường hợp này chỉ có 5 ngày trước khi Wiz Red Agent tìm ra. Thứ ba, “cổng bảo mật” trong workflow cần được kiểm tra theo đúng ngữ cảnh runtime, không phải chỉ đọc qua về mặt logic.
Để bảo vệ CI/CD pipeline của bạn trước GitHub Actions script injection vulnerability và các mối đe dọa tương tự: luôn dùng env: variable thay vì nội suy trực tiếp, triển khai static analysis tự động cho workflow files, và đặc biệt — hãy xem xét kỹ bất kỳ PR nào được tạo hoặc sửa đổi bởi AI, đặc biệt khi nó thay thế một pattern đã được thiết lập từ trước.
