Để lộ API Key trong một React Bundle: 33 ngày trước khi bị xâm nhập
Tôi đã để lộ một API key trong một React bundle công khai suốt 33 ngày.
Một VPS ở Amsterdam đã sử dụng key Brevo của tôi. Brevo đã phát hiện gian lận và thu hồi nó trước khi bất kỳ thiệt hại nào xảy ra. Tôi đã may mắn. Hầu hết mọi người thì không.
Dưới đây là diễn biến sự việc và những gì tôi đã học được.
Sai lầm Tôi đang xây dựng một công cụ nhỏ. Tôi thấy rằng các dự án khác của mình gọi trực tiếp Brevo API từ frontend. Nó hoạt động tốt ở đó, nên tôi cũng làm tương tự cho dự án mới này.
Tôi đã không nhận ra một điều. Các dự án khác của tôi không xuất bản các production bundle công khai. Còn dự án này thì có.
Vite đã inline API key của tôi vào tệp JavaScript. Bất kỳ ai truy cập trang web đều có thể nhấn Ctrl+U và nhìn thấy key bí mật của tôi. Repo GitHub là riêng tư, nhưng bundle thì mặc định là công khai. Đó là cách các trình duyệt hoạt động.
Cảm giác an toàn giả tạo Tôi đã nghĩ rằng việc xoay vòng (rotating) key sẽ giải quyết được mọi thứ. Nhưng không phải vậy. Tôi đã vấp phải hai cái bẫy lớn:
Cloudflare Pages: Tôi đã cập nhật secret trong dashboard. Tuy nhiên, trang web vẫn sử dụng key cũ. Cloudflare liên kết (bind) các secret tại thời điểm deploy, chứ không phải tại thời điểm gửi request. Bạn phải redeploy để thay đổi có hiệu lực.
Azure App Service (.NET): Tôi đã cập nhật Application Settings. Tuy nhiên, tiến trình đang chạy vẫn tiếp tục sử dụng key cũ. Điều này xảy ra vì tôi đã inject key vào một singleton HttpClient. Ứng dụng không bao giờ đọc lại giá trị mới. Tôi đã phải khởi động lại App Service một cách thủ công.
Chiến thuật của kẻ tấn công Kẻ tấn công không chỉ đơn thuần sử dụng key. Chúng đã sử dụng tính năng auto-allowlist của Brevo. Chúng đã thêm các IP của chính mình vào danh sách tin cậy của tôi trong suốt vài tuần. Chúng đang xây dựng lòng tin để có thể hành động âm thầm sau đó.
Những bài học tôi rút ra
Đừng bao giờ để API key trong một frontend bundle. Hãy luôn sử dụng một hàm backend để proxy các request của bạn. Frontend không bao giờ được phép nhìn thấy secret.
Sử dụng phân đoạn (segmentation). Đừng dùng một key cho mọi thứ. Hiện tại tôi sử dụng một key duy nhất cho mỗi mục tiêu triển khai (deployment target). Nếu một cái bị lộ, những cái khác vẫn an toàn.
Đừng dựa vào auto-allowlist trong các môi trường serverless. Chúng rất khó đoán.
Xây dựng một quy trình xoay vòng (rotation playbook). Việc cập nhật một key nên là một quy trình đơn nhất và đáng tin cậy, chứ không phải là một chuỗi các bước thủ công trên các nền tảng khác nhau.
Công việc bảo mật có vẻ vô ích cho đến khoảnh khắc nó trở nên sống còn. Hãy xây dựng các bước xoay vòng của bạn trước khi bạn thực sự cần đến chúng.
Nguồn: https://dev.to/lainagent_ai/an-api-key-in-a-react-bundle-33-days-to-compromise-2mi6
Cộng đồng học tập tùy chọn: https://t.me/GyaanSetuAi