Vibe coding và phần trách nhiệm không thể giao cho AI
Vibe coding giúp rút ngắn thời gian tạo phần mềm chạy được, nhưng không thay thế quá trình hiểu, kiểm chứng và thiết lập các ranh giới bảo mật cần thiết. AI có thể viết code và đề xuất kiểm thử, song con người vẫn phải chịu trách nhiệm xác định quyền hạn, áp dụng đặc quyền tối thiểu, kiểm tra cả hành vi bị từ chối và chỉ phát hành khi có đủ bằng chứng về độ an toàn.
9 giờ 17 phút tối, một developer yêu cầu AI thêm chức năng upload ảnh đại diện. Vài phút sau, giao diện đã hoạt động: chọn tệp, xem trước, bấm lưu. Test pass. Demo trôi chảy. Nhưng endpoint phía server không kiểm tra quyền sở hữu tài khoản, tên tệp được nối trực tiếp vào đường dẫn lưu trữ, còn bucket nhận ảnh cho phép đọc công khai.
Không lỗi nào trong số đó nhất thiết xuất hiện trên màn hình của người đang viết code. Chúng chỉ hiện ra khi có người đặt những câu hỏi khác: ai được phép thay ảnh của ai, dữ liệu nào được tin cậy, tiến trình đang có quyền gì, và điều gì xảy ra nếu request được tạo bằng curl thay vì đi qua giao diện?
Đây là điểm khiến security trở nên đặc biệt khó trong thời đại vibe coding. AI có thể rút ngắn đáng kể quãng đường từ ý tưởng tới phần mềm chạy được, nhưng tốc độ ấy không tự rút ngắn quãng đường từ phần mềm chạy được tới phần mềm có thể được tin cậy. Hai đích đến này vốn đã khác nhau. Vibe coding chỉ làm khoảng cách giữa chúng dễ bị bỏ qua hơn.
Demo trơn tru không phải là bằng chứng về security
Trong bài này, tôi dùng vibe coding để chỉ cách phát triển trong đó người làm phần mềm mô tả mục tiêu bằng ngôn ngữ tự nhiên, để AI tạo hoặc sửa code, rồi điều hướng quá trình chủ yếu qua kết quả quan sát được. Đây không nhất thiết là hành vi cẩu thả. Với prototype, script nội bộ hoặc phần giao diện có phạm vi hẹp, cách làm ấy có thể rất hiệu quả.
Vấn đề xuất hiện khi cảm giác “đã chạy đúng” được dùng làm bằng chứng cho một mệnh đề rộng hơn: “hệ thống đủ an toàn”. Một luồng đăng nhập thành công chỉ cho thấy tài khoản hợp lệ có thể đăng nhập trong trường hợp vừa thử. Nó chưa cho biết session có được vô hiệu hóa đúng lúc hay không, endpoint có kiểm tra authorization ở phía server hay không, token có bị ghi vào log hay không, hoặc một người dùng có thể sửa identifier để đọc dữ liệu của người khác hay không.
Security quan tâm nhiều đến những hành vi hệ thống phải từ chối. Trong khi đó, một phiên vibe coding thường được dẫn dắt bằng hành vi mong muốn: tạo tài khoản, thanh toán, upload tệp, gửi email. Nếu prompt chỉ mô tả đường đi thuận lợi, code được tạo ra có thể hoàn thành chính xác đường đi ấy mà không xác lập đầy đủ các ranh giới xung quanh nó.
OWASP xếp Broken Access Control ở vị trí đầu tiên trong Top 10:2025. Tài liệu này nhấn mạnh rằng kiểm soát truy cập phải được thực thi trong code phía server mà phía tấn công không thể sửa đổi, đồng thời khuyến nghị deny by default và kiểm tra quyền sở hữu bản ghi.1 Những yêu cầu đó không dễ suy ra từ một ảnh chụp giao diện hoặc một demo thành công. Chúng đòi hỏi người xây dựng phải mô tả cả điều được phép lẫn điều bị cấm.
Code sinh ra nhanh, nghĩa vụ hiểu không biến mất
Có một sự bất đối xứng đáng chú ý: AI có thể tạo thêm hàng trăm dòng code trong vài phút, nhưng năng lực review của con người không tăng theo cùng tốc độ. Khi thay đổi còn nhỏ, developer có thể đọc từng dòng và giữ được mô hình tương đối đầy đủ về luồng dữ liệu. Khi diff lớn dần, việc review dễ chuyển thành nhận dạng bề mặt: tên hàm hợp lý, cấu trúc quen mắt, test hiện có vẫn pass.
Code hợp lý về hình thức chưa phải code có thuộc tính security đúng. Một middleware tên requireAuth có thể chỉ kiểm tra sự tồn tại của token mà không xác minh chữ ký. Một hàm sanitizeFilename có thể loại khoảng trắng nhưng vẫn chấp nhận chuỗi traversal. Một API có thể xác thực người gọi nhưng không kiểm tra người đó có quyền thao tác trên bản ghi cụ thể hay không.
Một nghiên cứu năm 2021 của Pearce và cộng sự đã tạo 1.689 chương trình từ 89 kịch bản để khảo sát code do một phiên bản GitHub Copilot thời kỳ đầu đề xuất; khoảng 40% chương trình trong thiết lập của họ chứa lỗ hổng.2 Con số này không nên được ngoại suy thành tỷ lệ lỗi của mọi model hiện nay: công cụ, dữ liệu huấn luyện và cơ chế hỗ trợ đã thay đổi đáng kể. Giá trị lâu dài của nghiên cứu nằm ở quan sát thận trọng hơn: code được sinh ra có thể trông đúng, chạy được và vẫn tái tạo những mẫu không an toàn có trong dữ liệu hoặc ngữ cảnh đầu vào.
Ngay cả tài liệu sử dụng GitHub Copilot cũng yêu cầu người dùng review và test kỹ code được tạo cho các ứng dụng nhạy cảm về security.3 AI hỗ trợ quá trình viết; nó không chuyển trách nhiệm về thuộc tính của hệ thống sang nhà cung cấp model.
Security là vấn đề về ranh giới
Nhiều lỗ hổng không bắt đầu từ một dòng code hiển nhiên nguy hiểm. Chúng bắt đầu từ một ranh giới chưa được diễn đạt:
-
Đâu là dữ liệu không đáng tin cậy?
-
Danh tính nào được phép thực hiện hành động này?
-
Quyền hạn tồn tại trong bao lâu và bị thu hồi bằng cách nào?
-
Thành phần nào có thể đọc secret, gọi mạng hoặc ghi vào database?
-
Nếu một bước thất bại giữa chừng, hệ thống còn lại trạng thái nào?
Những câu hỏi này gần với thiết kế hơn là cú pháp. Bởi vậy, prompt “thêm API xóa tài khoản” còn thiếu phần quan trọng nhất của yêu cầu. Xóa tài khoản nào? Ai được gọi? Có cần xác nhận lại danh tính không? Dữ liệu liên quan được xóa, ẩn hay giữ lại? Request lặp lại có gây tác dụng phụ không? Audit log cần ghi gì nhưng không được ghi secret nào?
Từ năm 1975, Saltzer và Schroeder đã mô tả các nguyên tắc như fail-safe defaults, complete mediation và least privilege.4 Các nguyên tắc ấy vẫn phù hợp với vibe coding vì chúng buộc người xây dựng chuyển từ việc hỏi “tính năng có hoạt động không?” sang “quyền nào đang được cấp, ở đâu, cho ai và trong điều kiện nào?”. AI có thể đề xuất cách triển khai một ranh giới, nhưng con người vẫn phải quyết định ranh giới đó nằm ở đâu.
flowchart LR
A[Ý định tính năng] --> B[Ranh giới tin cậy]
B --> C[Code do AI đề xuất]
C --> D[Review theo threat model]
D --> E[Test hành vi được phép]
D --> F[Test hành vi bị từ chối]
E --> G[Quyết định phát hành]
F --> G
Niềm tin không nằm trong một file
Vibe coding thường tạo cảm giác rằng artifact cần đánh giá là phần code vừa xuất hiện trong editor. Trên thực tế, phần mềm còn phụ thuộc vào package, image, workflow CI/CD, secret, quyền của service account, cấu hình cloud và dịch vụ bên ngoài. Một đoạn code ngắn có thể đưa vào dependency với hàng chục dependency bắc cầu; một lệnh cấu hình có thể mở quyền ghi rộng hơn nhiều so với chức năng yêu cầu.
Ken Thompson từng chỉ ra trong Reflections on Trusting Trust rằng việc đọc source code chưa đủ để thiết lập niềm tin nếu compiler hoặc chuỗi công cụ có thể chèn hành vi không hiện diện trong source.5 Lập luận này không nói rằng mọi tool đều đáng ngờ. Nó nhắc rằng niềm tin phần mềm luôn có tính bắc cầu: ta tin code, đồng thời tin các công cụ đã tạo, build và vận hành code ấy.
Với AI coding agent có quyền chạy command, cài package hoặc sửa workflow, chuỗi niềm tin còn mở rộng tới tool call. Prompt khi đó không chỉ tạo văn bản; nó có thể dẫn tới thay đổi trạng thái thật. Việc giới hạn quyền đọc, ghi, truy cập mạng và sử dụng secret vì thế không phải trở ngại phụ. Đó là một phần của thiết kế security.
Nguyên tắc least privilege nên được áp dụng ngay vào môi trường coding: agent chỉ được đọc những gì cần thiết, chỉ được ghi trong phạm vi đã phê duyệt, và không mặc nhiên có credential production. Nếu một tác vụ frontend không cần truy cập database, không có lý do để môi trường thực thi mang quyền đó. Quyền không được cấp thì không thể bị một prompt sai hoặc một tool call ngoài dự kiến sử dụng.
Một quy trình chậm có chủ đích ở đúng chỗ
Câu trả lời hợp lý không phải là từ bỏ vibe coding. Ta có thể giữ tốc độ tạo code, nhưng phải đặt ràng buộc con ngừơi ở những quyết định có thể mang đến hậu quả lớn. NIST SSDF coi secure development là tập hợp thực hành cần được tích hợp vào vòng đời phát triển, thay vì một hoạt động tách rời ở cuối quy trình.6 Với vibe coding, cách nhìn ấy có thể được cụ thể hóa thành vài kỷ luật.
Trước khi prompt về implementation, hãy viết một threat model ngắn: tài sản cần bảo vệ, tác nhân có thể tương tác, trust boundary và các hành vi phải bị từ chối. Không cần biến mọi thay đổi thành một buổi họp dài. Với endpoint đơn giản, năm câu hỏi đúng thường có giá trị hơn năm trang tài liệu chung chung.
Tiếp đó, chia thay đổi thành diff đủ nhỏ để đọc. Review không chỉ nhằm tìm dòng code đáng ngờ mà còn để lần theo dữ liệu: input đi từ đâu, được kiểm tra tại đâu, tới interpreter hay storage nào, và authorization được thực thi trước tác dụng phụ hay sau đó. Với SQL, shell, template hoặc URL, cần kiểm tra liệu dữ liệu không đáng tin cậy có bị đưa vào vị trí có thể thay đổi cấu trúc lệnh hay không. OWASP mô tả injection chính bằng quan hệ này giữa input không đáng tin cậy và interpreter.7
Sau cùng, test phải bao gồm các trường hợp từ chối: thiếu token, token hết hạn, sai role, sửa object ID, gửi payload quá lớn, lặp request, giả lập dependency timeout và làm gián đoạn thao tác giữa chừng. SAST, dependency scanning và secret scanning hữu ích, nhưng chúng không thay thế test cho business rule. Một scanner có thể phát hiện chuỗi SQL được nối trực tiếp; nó khó biết nhân viên chi nhánh A không được duyệt đơn của chi nhánh B nếu quy tắc ấy chưa được mã hóa thành policy và test.
Người chịu trách nhiệm phải giữ quyền nói “KHÔNG”
Điều đáng lo nhất ở vibe coding không phải việc developer gõ ít code hơn. Nghề làm phần mềm chưa bao giờ được định nghĩa bằng số ký tự tự tay nhập. Rủi ro nằm ở chỗ quá trình tạo ra kết quả thuyết phục có thể diễn ra nhanh hơn quá trình hình thành hiểu biết cần thiết để chịu trách nhiệm cho kết quả đó.
Trách nhiệm ở đây không đòi hỏi một người phải nhớ toàn bộ codebase. Nó đòi hỏi khả năng đưa ra bằng chứng phù hợp với mức rủi ro: biết trust boundary nằm ở đâu, biết quyền nào đã được cấp, biết test nào chứng minh hành vi bị từ chối, và biết điều gì vẫn chưa được kiểm chứng. Khi chưa có các bằng chứng ấy, trạng thái thực tế của thay đổi do Agent sinh ra phải là “chưa sẵn sàng”, dù cho demo đã trơn tru.
Vibe coding làm cho phần mềm trở nên dễ khởi tạo hơn. Chính vì vậy, quyết định đưa phần mềm vào nơi có dữ liệu thật, tiền thật và con người thật càng cần được tách khỏi cảm giác hứng khởi lúc prototype vừa chạy. AI có thể viết code, đề xuất test và chỉ ra lỗ hổng. Nhưng quyền nói “đủ an toàn để phát hành” vẫn phải đi cùng một chủ thể sẵn sàng giải thích mình đã dựa vào bằng chứng nào.
Tài liệu tham khảo
- OWASP Foundation (2025). A01:2025 Broken Access Control. OWASP Top 10:2025
- Hammond Pearce, Baleegh Ahmad, Benjamin Tan, Brendan Dolan-Gavitt và Ramesh Karri (2021). Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions. arXiv:2108.09293
- GitHub (không ghi năm). Responsible use of GitHub Copilot Chat in GitHub. GitHub Docs
- Jerome H. Saltzer và Michael D. Schroeder (1975). The Protection of Information in Computer Systems. Proceedings of the IEEE, 63(9), 1278–1308
- Ken Thompson (1984). Reflections on Trusting Trust. Communications of the ACM, 27(8), 761–763
- Murugiah Souppaya, Karen Scarfone và Donna Dodson (2022). Secure Software Development Framework (SSDF) Version 1.1: Recommendations for Mitigating the Risk of Software Vulnerabilities. NIST SP 800-218
- OWASP Foundation (2025). A05:2025 Injection. OWASP Top 10:2025