- Ngày 3 tháng 8 năm 2025, trang web DrawAFish! xảy ra một sự cố bảo mật quy mô lớn kéo dài khoảng 6 giờ
- Rò rỉ mật khẩu quản trị viên, API thiếu xác thực, lỗ hổng JWT và các lỗ hổng bảo mật cốt lõi khác đã cùng lúc bị khai thác trong vụ tấn công
- Hệ quả là mọi tên người dùng đều bị thay đổi thành các cụm từ xúc phạm và hình vẽ phản cảm được phê duyệt cùng lúc với việc xóa các hình vẽ an toàn trước đó
- Vấn đề được khắc phục bằng khôi phục thủ công, sửa đổi logic xác thực, kiểm tra lại bản sao lưu
- Bài học chính là phát triển nhanh (“vibe-coding”) và thiếu kiểm thử, code review có thể gây ra hậu quả bảo mật nghiêm trọng
Tổng quan DrawAFish.com và tóm tắt sự cố ngày 3/8/2025
- DrawAFish.com là một trang web cho phép người dùng tự vẽ cá và cùng nhau cho cá bơi trong bể
- Ngày 1/8/2025, trang web lọt vào vị trí số 1 trên Hacker News, nhận được sự chú ý lớn. Nhà phát triển đã áp dụng cách làm nhanh “vibe-coding” với Copilot và các công cụ tương tự để triển khai tính năng
- Tuy nhiên, rạng sáng ngày 3/8/2025 đã xảy ra một cuộc tấn công bảo mật nghiêm trọng
- Trong khoảng 6 giờ, tên người dùng bị thay đổi thành các cách diễn đạt dung tục, và tình trạng hỗn loạn bùng phát với việc phê duyệt tranh thiếu phù hợp và các hình vẽ không phù hợp
- Cuối cùng, quản trị viên đã thực hiện thao tác khôi phục thủ công
Phân tích chi tiết lỗ hổng
1. Mật khẩu quản trị viên 6 ký tự đã từng bị rò rỉ
- Ban đầu, nhà phát triển đã dùng ID thời thơ ấu của mình và mật khẩu 6 ký tự cho tài khoản quản trị viên
- Mật khẩu này đã bị lộ công khai trên mạng thông qua các vụ rò rỉ dữ liệu từ các trang như Neopets
- Dù sau đó nhà phát triển đã chuyển sang Google Auth, việc không xóa mật khẩu cũ đã để lại lỗ hổng cho kẻ tấn công
- Kẻ tấn công đã dùng thông tin rò rỉ để đăng nhập thành công vào xác thực quản trị, rồi thực hiện hành vi ác ý như phê duyệt hình ảnh xúc phạm và xóa hình ảnh bình thường
2. API thay đổi tên người dùng không có xác thực
- Khi phát triển backend hồ sơ, do lý do “phát triển nhanh”, nhà phát triển đã triển khai API đổi tên người dùng mà không có kiểm tra xác thực
- Thực tế, bất kỳ ai cũng có thể thay đổi tên người dùng tùy ý
3. Kiểm tra JWT chưa đầy đủ
- Trong xác thực dựa trên token JWT, việc thao tác quản trị được cho phép mà không đối chiếu giữa token với userId/email
- Nghĩa là, nếu kẻ tấn công có token được cấp với thông tin đăng nhập quản trị thì token đó có thể được dùng như quyền quản trị cho bất kỳ yêu cầu nào
- Điều thú vị là một người dùng trên Hacker News đã tận dụng lỗ hổng này để xóa nội dung không phù hợp trước khi kẻ xâm nhập làm vậy, góp phần hỗ trợ ứng phó khẩn cấp
Quá trình khôi phục
- Khoảng 7 giờ 45 phút sáng, nhà phát triển nhận ra tình hình đã nghiêm trọng và bắt đầu ứng phó ngay trên máy tính để bàn
- Vì chưa bật sao lưu Firebase, nên không thể dựa vào backup; do đó đã nhanh chóng sửa mã để ép buộc xác thực
- Theo dõi toàn bộ nhật ký kiểm duyệt để phát triển script hủy bỏ các thao tác ác ý (script này cũng được viết theo phong cách vibe-coding)
- Tài khoản truy cập của bên thứ ba (người dùng Hacker News) đã sử dụng quyền admin trong tình huống khẩn cấp cũng bị chặn, sau đó liên hệ để nhận phản hồi về việc refactor bảo mật cho codebase và tiến hành vá thêm
Văn hóa phát triển và bài học
- Nhà phát triển trải nghiệm rằng “vibe-coding”, tức nhanh tạo mẫu và kiểm tra tối thiểu, dù mang lại niềm vui và năng suất cao, nhưng thực tế có thể dẫn đến lỗ hổng bảo mật nghiêm trọng
- Mặc dù LLM (Copilot) rất hữu ích cho năng suất viết mã nhanh, nó cũng nhấn mạnh trách nhiệm về chất lượng và bảo mật của mã vẫn thuộc về chính nhà phát triển
- Việc bỏ qua các bước cơ bản như kiểm thử, logic xác thực và code review đã cho thấy ngay cả dự án nhỏ cũng có thể trở nên cực kỳ dễ bị tấn công từ bên ngoài
Kết luận và góc nhìn
- Vụ việc của DrawAFish.com cho thấy tầm quan trọng của các biện pháp bảo mật cơ bản thường bị bỏ qua trong vận hành dịch vụ thực tế
- Ngay cả khi phụ thuộc vào công cụ mã nguồn mở và công cụ phát triển nhanh, cần bắt buộc kiểm tra kiểm thử, xác thực, code review và quản lý mật khẩu như các hạng mục cơ bản
- Mức độ chú ý tăng vọt bất ngờ (ví dụ: xếp hạng cao trên Hacker News) có thể làm mở rộng nhanh chóng diện tấn công và rủi ro
- Việc ghi lại postmortem một cách minh bạch giúp truyền tải bài học bảo mật thực tế cho các startup và nhà phát triển cá nhân tương lai
3 bình luận
Ý kiến Hacker News
Mình cũng thích làm việc rất nhanh; việc không làm code review rồi cứ đẩy code liên tục mới thú vị, nhưng khi đọc postmortem này thì mình hiểu vì sao side project của mình hay bị dừng giữa chừng: cuối cùng lại vướng vào phần công việc thực tế chán ngắt và dừng lại ở đó.
Mình tò mò không biết có phải Firebase không: là do dùng mãi free tier, hay có ai đó tấn công ác ý khiến buổi sáng thức dậy lại thấy hóa đơn năm chữ số.
Mình là một trong những người “may mắn” đã trải qua trực tiếp vụ Slurpy Fish; làm bảo mật nên vẫn phải cười trước sự hiển nhiên quá mức của nó, nhưng mình biết là HN sớm muộn gì cũng có người có thời gian để can thiệp đúng lúc. Việc một dự án vibe-coded cũng có bài postmortem được ghi chép chi tiết như thế này thật tuyệt, vì trong IR dạo gần đây mình thấy production toàn là code kiểu theo vibe, nên thấy ở một dự án nhỏ như thế này được tóm tắt gọn gàng cũng rất đáng chú ý.
Postmortem này đặc biệt hấp dẫn vì là app vibe-coded. Tò mò không biết có lấy cảm hứng từ triển lãm làm cá ở Lego House không; mình vừa ghé qua đó gần đây và việc tự tạo ra một chú cá rồi thấy nó bơi cùng các chú cá khác thật sự rất gây nghiện. Video Build-a-Fish của Lego House trên YouTube
Thật khó tin khi có người cố gắng chặn một cuộc tấn công thật bằng cách khai thác chính lỗ hổng bảo mật.
“S” trong vibe-coded là chữ viết tắt của Security.
Khi bạn nghĩ ra ý tưởng hoàn toàn mới, tự tay làm, học được rất nhiều rồi vẫn biết xấu hổ vì thất bại theo cách chuyên nghiệp, đó mới là chuyên gia thực thụ; nếu không mất 100.000 USD, không làm hỏng mạng và không mất việc thì sau một năm bạn sẽ cười nhắc lại những chuyện này.
Bây giờ trên trang đó có cá Swastika, ai đó chèn nó vào hình dạng cá để bypass bộ lọc. Ảnh gây tranh cãi, hiện đã bị xóa.
Hiện tại có thể vote bất kỳ cá nào mà không cần xác thực, tối đa 20 lần/phút/IP, qua POST tới endpoint này API vote {"fishId":"xxxx","vote":"up"}
Mình thật sự thích khi có bài postmortem về một website vibe-coded. Khi quá nhiều người nhìn công nghệ một cách quá nghiêm túc, góc nhìn nhẹ nhàng và vui vẻ này thật mới mẻ, và từ góc nhìn một side project nhỏ thì cực kỳ thú vị và có ý nghĩa.