📌 Mục lục

  1. Vì sao phải học bảo mật?
  2. RBAC là gì? — Câu chuyện tòa nhà văn phòng
  3. RLS là gì? — Câu chuyện tủ hồ sơ cá nhân
  4. RBAC vs RLS — Khi nào dùng cái nào?
  5. Firebase Auth — Cổng vào tòa nhà
  6. Firestore Security Rules — Bảo vệ tủ hồ sơ
  7. Áp dụng vào ProjectOS — Ai làm được gì?
  8. Thư viện Prompt mẫu cho Claude Code
  9. Thực hành: 5 bài tập có lời giải prompt
  10. Checklist kiểm tra sau khi Claude Code viết xong

1. Vì sao phải học bảo mật?

Hãy tưởng tượng bạn vừa thuê Claude Code xây xong ứng dụng ProjectOS. App chạy ngon, đẹp, ai cũng vào được. Nhưng có vấn đề:
  • 👤 Một nhân viên thử vọc → vô tình xoá toàn bộ task của Giám đốc.
  • 🕵️ Một thực tập sinh tò mò → đọc được bảng lương của cả công ty.
  • 😈 Một cựu nhân viên (vẫn còn tài khoản) → sửa ngân sách dự án từ ở nhà.
→ Tất cả đều xảy ra vì ứng dụng không có lớp bảo vệ.
Quy tắc vàng: Trong Firebase, không có server backend riêng. Trình duyệt của người dùng nói chuyện thẳng với Firestore. Nếu không có luật bảo mật, bất kỳ ai biết một chút kỹ thuật đều có thể đọc/sửa/xoá mọi thứ.
Có 2 lớp bảo vệ chính ta cần dựng lên:

2. RBAC là gì?

🏢 Câu chuyện toà nhà văn phòng

Hãy hình dung công ty SOFTECH có một toà nhà 10 tầng:
  • Tầng 1 — Sảnh: Ai cũng vào được.
  • Tầng 2-5 — Văn phòng nhân viên: Cần thẻ nhân viên.
  • Tầng 6-8 — Phòng họp & tài liệu mật: Cần thẻ trưởng phòng trở lên.
  • Tầng 9 — Phòng tài chính: Chỉ kế toán + giám đốc.
  • Tầng 10 — Phòng giám đốc: Chỉ giám đốc.
Bảo vệ ở sảnh không nhớ mặt từng người — họ chỉ nhìn màu thẻ: Đó chính là RBAC. Mỗi người được gán một vai trò (role), và mỗi vai trò có một danh sách quyền cố định.

🎯 RBAC trong app

Áp dụng vào ProjectOS:

🗝️ Role được lưu ở đâu?

Trong Firebase, role thường được lưu ở 2 nơi:
  1. Custom Claims (gắn vào tài khoản Auth) — Cách “xịn”, không sửa được từ trình duyệt.
  2. Document users/{userId} trong Firestore — Dễ làm, nhưng phải viết luật cẩn thận để người dùng không tự sửa role của mình.
💬 Khi prompt Claude Code, bạn chỉ cần nói: “Lưu role ở custom claims” hoặc “Lưu role trong collection users” — Claude sẽ tự chọn cách triển khai.

3. RLS là gì?

📁 Câu chuyện tủ hồ sơ cá nhân

Quay lại toà nhà SOFTECH. Bạn là nhân viên (member), bạn đã lên được tầng 3. Trong phòng có 100 tủ hồ sơ — mỗi tủ là một task của một người khác nhau.
  • Bạn chỉ được mở tủ của chính bạn (task assignee = bạn).
  • Bạn không được mở tủ của anh A ngồi cạnh — dù bạn đã vào được tầng.
Đó là RLS (Row-Level Security): bảo vệ ở mức từng dòng dữ liệu, không phải mức “có vào được hay không”.

🔍 RLS trả lời câu hỏi gì?

💡 Sự khác biệt mấu chốt


4. RBAC vs RLS

Trong thực tế, luôn dùng cả hai cùng lúc. Đây là cách kết hợp: Khi Tùng gọi Firestore, Firebase tự đọc role: manager từ vé.

💬 Prompt mẫu để Claude Code thiết lập custom claims:

Tôi cần một Firebase Cloud Function để admin có thể gán role (viewer, member, manager, finance, admin) cho user khác qua custom claims. Yêu cầu:
  • Chỉ user có role == admin mới gọi được function này.
  • Function nhận targetUserIdnewRole.
  • Validate newRole phải nằm trong danh sách cho phép.
  • Cập nhật cả custom claim VÀ document users/{targetUserId}.role để FE đọc nhanh.
  • Viết theo Cloud Functions v2 (onCall), TypeScript, có log audit.

6. Firestore Security Rules

Đây là “bộ luật” mà Firebase đọc mỗi khi có ai cố đọc/ghi dữ liệu. Nó được viết bằng một ngôn ngữ riêng (gần giống JavaScript) và lưu trong file firestore.rules.

📜 Cấu trúc cơ bản

🔍 Bóc tách từng dòng

⚠️ 3 cái bẫy hay gặp

  1. request.resource vs resource — Lẫn lộn 2 cái này = mở toang cửa.
    • resource = dữ liệu đã có trong DB.
    • request.resource = dữ liệu người dùng muốn ghi vào.
  2. Quên kiểm tra trường nhạy cảm khi update. Ví dụ: Cho member sửa task — nhưng không cấm họ tự đổi role thành admin.
  3. Cho client tự ghi role. Nếu role lưu trong Firestore và bạn cho user update document users/{me}, họ có thể tự thăng chức! → Luôn dùng request.resource.data.role == resource.data.role để cấm thay đổi.

7. Áp dụng vào ProjectOS

Đây là ma trận quyền đầy đủ cho ProjectOS. Đưa bảng này cho Claude Code, nó sẽ viết được rules.

📊 Bảng quyền

Ghi chú ký hiệu: 👁️ đọc | ✏️ ghi/sửa | 🗑️ xoá | ❌ chặn hoàn toàn

🔑 Quy tắc đặc biệt

  • role trong users/{uid} — không user nào tự sửa được, kể cả chính họ. Chỉ admin qua Cloud Function.
  • Budget — bị chặn hoàn toàn với viewer/member (kể cả đọc).
  • Activity log — chỉ Cloud Function được ghi, FE chỉ đọc.

8. Thư viện Prompt mẫu

Đây là 8 prompt sẵn dùng — copy-paste vào Claude Code khi bạn cần.

📝 Prompt 1 — Thiết lập role system ban đầu

📝 Prompt 2 — Viết Firestore Rules tổng thể

📝 Prompt 3 — Cloud Function gán role

📝 Prompt 4 — Test rules bằng Emulator

📝 Prompt 5 — UI: Ẩn nút theo role

📝 Prompt 6 — Phát hiện rò rỉ quyền

📝 Prompt 7 — Audit log

📝 Prompt 8 — Migrate role cũ


9. Thực hành

Đây là 5 bài tập có lời giải prompt. Học viên đọc đề → tự nghĩ cách diễn đạt → so sánh với prompt mẫu.

🎯 Bài 1 — Cấm intern xem lương

Đề: Trong ProjectOS có module Team, mỗi member có field salary. Bạn muốn:
  • Member chỉ xem được lương của chính mình.
  • Manager xem được lương của mọi người trong team.
  • Finance/Admin xem được tất cả.
Prompt gợi ý:

🎯 Bài 2 — Một user vừa là member dự án A, vừa là manager dự án B

Đề: ProjectOS hiện đang single-project. Sau này khi mở rộng multi-project, một user có thể có role khác nhau ở từng dự án. Prompt gợi ý:

🎯 Bài 3 — Cho phép guest xem dashboard public

Đề: Bạn muốn có một trang /public/{projectId} mà ai (kể cả chưa login) cũng xem được — nhưng chỉ thấy số liệu tổng quan, không thấy task chi tiết. Prompt gợi ý:

🎯 Bài 4 — Xoá task chỉ trong 5 phút sau khi tạo

Đề: Để chống “lỡ tay”, chỉ cho phép xoá task trong vòng 5 phút sau khi tạo, và chỉ bởi người tạo. Sau 5 phút, chỉ manager mới xoá được. Prompt gợi ý:

🎯 Bài 5 — Khoá tài khoản tạm thời

Đề: Khi một nhân viên bị nghỉ phép kỷ luật, admin muốn vô hiệu hoá tài khoản mà không xoá data. Prompt gợi ý:

10. Checklist kiểm tra

Sau khi Claude Code viết xong, luôn chạy checklist này trước khi deploy:

✅ Checklist bảo mật Firestore

  • Default deny: File firestore.rules có dòng allow read, write: if false; ở cuối không?
  • Không có allow read, write: if true ở bất kỳ đâu (trừ khi có lý do rất rõ).
  • Trường role trong users/{uid} không thể bị client tự sửa (test bằng emulator).
  • Custom claims được set qua Cloud Function, không qua FE.
  • Mọi collection đều có rule explicit (không rơi vào “không có rule = mở toang”).
  • Phân biệt resource vs request.resource trong update rules.
  • Index đã tạo cho mọi query có where + orderBy (chạy app, đọc warning ở console).
  • Test với emulator cho ít nhất 5 kịch bản chính (xem Prompt 4).
  • Audit log đang ghi cho hành động nhạy cảm (đổi role, sửa budget…).
  • UI guard đồng bộ với Rules — không có nút “Delete” mà rule lại từ chối (gây confused UX).

⚠️ Cảnh báo cuối: Rules không thay thế UI

Có người tưởng “tôi đã ẩn nút Delete ở UI, rule không cần lo”. SAI HOÀN TOÀN. → Bất kỳ ai cũng có thể mở DevTools, gọi Firestore SDK trực tiếp từ console, bypass UI. → Rules là tuyến phòng thủ THẬT. UI guard chỉ là UX cho người dùng tốt bụng.

🎓 Tóm tắt 1 trang

RBAC = “Bạn là ai?” → role: viewer/member/manager/finance/admin RLS = “Cái này có phải của bạn không?” → so sánh uid với field trong document Firebase Auth = cấp ID Token + Custom Claims (vé có ghi role) Firestore Rules = bộ luật ở server, đọc vé + kiểm tra điều kiện Cloud Functions = nơi duy nhất an toàn để gán/thay đổi role
Quy trình prompt Claude Code:
  1. Mô tả ai được làm với dữ liệu nào (dạng bảng càng tốt).
  2. Yêu cầu Claude đề xuất 2-3 phương án trước khi code.
  3. Sau khi code xong, yêu cầu Claude viết test bằng emulator.
  4. Chạy checklist 10 mục ở mục 10.
  5. KHÔNG BAO GIỜ deploy rules mới mà chưa test bằng emulator.

Tài liệu kèm theo trong repo ProjectOS:
  • .claude/docs/auth.md — Auth flow chi tiết
  • .claude/docs/firebase.md — Schema thực tế
  • .claude/agents/firebase-expert.md — Agent chuyên Firestore
Chúc bạn xây ProjectOS an toàn! 🔐