Vault· Tài liệu
Quay lại vault

Tổng quan & Bảo mật

Vault là kho lưu trữ thông tin nhạy cảm cho cá nhân: mật khẩu đăng nhập, private key ví, seed phrase, SSH key, ghi chú bảo mật, API key. Toàn bộ việc mã hóa và giải mã diễn ra ngay trong trình duyệt của bạn. Máy chủ (Supabase) chỉ lưu dữ liệu đã mã hóa và không bao giờ thấy nội dung gốc.

Zero-knowledge nghĩa là gì?

Master password của bạn không bao giờ rời khỏi trình duyệt. Kẻ tấn công chiếm được cơ sở dữ liệu Supabase cũng chỉ lấy được các khối ký tự vô nghĩa (ciphertext). Đây chính là mô hình mà Bitwarden và 1Password dùng.

Mã hóa kiểu "phong bì" (envelope)

Một khóa dữ liệu ngẫu nhiên 256-bit (gọi là DEK) mã hóa mọi mục bằng AES-256-GCM. DEK được "bọc" lại bởi hai khóa khác nhau: một khóa dẫn từ master password (qua Argon2id), và một khóa dẫn từ recovery key 24 từ. Đổi master password chỉ cần bọc lại DEK, không phải mã hóa lại toàn bộ dữ liệu.

Master password
Recovery key (24 từ)
Argon2id (KDF)
KEK_master
KEK_recovery
bọc (wrap) DEK
DEK - khóa dữ liệu 256-bit (chỉ ở RAM)
AES-256-GCM, mỗi mục 1 IV ngẫu nhiên
Ciphertext { iv, ct } → lưu lên Supabase
Khóa / bí mậtPlaintext (chỉ ở trình duyệt)Ciphertext (lưu Supabase)
authSecret (mật khẩu đăng nhập Supabase) cũng dẫn từ master + email, tách biệt hoàn toàn với KEK.

Thuật toán

  • Argon2id (64 MiB, 3 vòng) biến mật khẩu thành khóa, chống brute-force mạnh.
  • AES-256-GCM mã hóa từng mục; thẻ xác thực (auth tag) tự động từ chối nếu dữ liệu bị sửa hoặc sai khóa.
  • Recovery key dùng chuẩn BIP39 (24 từ tiếng Anh).
Không có nút "Quên mật khẩu"
Đây là cái giá của bảo mật tối đa. Mất master password thì chỉ khôi phục được bằng recovery key + file backup. Mất cả hai thì dữ liệu mất vĩnh viễn, không ai (kể cả bạn) lấy lại được.

Luồng lưu trữ & mở khóa

Khi bạn lưu một mục

Dữ liệu được mã hóa ngay tại trình duyệt bằng DEK trước khi gửi đi. Supabase chỉ nhận về ciphertext, kèm hai trường công khai duy nhất là typefavorite (để đếm và lọc theo nhóm mà không cần giải mã).

Bạn nhập form (title, password, ...)
encryptJSON(DEK) trong trình duyệt
{ iv, ciphertext }
INSERT (RLS)
Supabase vault_items: type, favorite, encrypted_data, iv
Không có plaintext nào đi qua máy chủ Next.js.

Khi bạn mở khóa

Mật khẩu đăng nhập Supabase được dẫn từ master + email nên tính được ngay mà không cần đọc cơ sở dữ liệu trước. Sau khi đăng nhập, ứng dụng tải cấu hình (vẫn là ciphertext), dẫn ra KEK và mở khóa DEK.

Master password + email
Argon2id (salt = SHA-256(email))
authSecret
đăng nhập Supabase
Tải vault_config (ciphertext)
Argon2id(salt_master) → mở khóa
KEK_master → unwrap DEK
tải + giải mã trong RAM
Danh sách mục đã giải mã
DEK chỉ tồn tại trong RAM của trình duyệt khi vault đang mở.
Vì sao salt lấy từ email?
Nếu salt đăng nhập nằm trong cấu hình thì có nghịch lý "con gà - quả trứng": phải đăng nhập mới đọc được cấu hình, nhưng phải có salt trong cấu hình mới đăng nhập được. Lấy salt từ email (công khai, chỉ dùng làm salt) phá vỡ vòng lặp đó. Bảo mật không đổi vì khóa thật sự bảo vệ dữ liệu là KEK với salt ngẫu nhiên trong DB.

Các lớp bảo vệ khác

  • RLS nghiêm ngặt: mọi dòng gắn auth.uid() = user_id; lộ anon key cũng chỉ đọc được ciphertext.
  • Tự khóa: xóa DEK khỏi RAM sau thời gian rảnh hoặc khi chuyển tab (tùy chỉnh trong Settings).
  • Tìm kiếm trong bộ nhớ: giải mã một lần khi mở khóa rồi tìm fuzzy ngay tại client, không lộ gì cho máy chủ.
  • Tự xóa clipboard: sau khi copy, clipboard được xóa sau 10-30 giây (tùy chỉnh), kèm đồng hồ đếm ngược.

Triển khai A-Z (Vercel + Supabase)

Toàn bộ quy trình từ con số 0 đến khi app chạy thật trên Vercel. Cần một tài khoản Supabase (miễn phí) và một tài khoản Vercel.

1. Tạo Supabase project
2. Điền .env.local
3. Tắt Confirm email
4. Chạy migration (tạo bảng + RLS)
5. verify:supabase (kiểm tra)
6. dev (local) → 7. deploy Vercel
Quy trình triển khai.
  1. 1
    Tạo Supabase project
    Vào supabase.com, tạo project mới. Mở Project Settings → API, copy Project URLanon public key. Tuyệt đối không dùng service_role key trong app.
  2. 2
    Điền .env.local
    Sao chép .env.local.example thành .env.local rồi điền 3 biến (biến tùy chọn cuối chỉ cần cho lệnh db:reset):
    NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
    NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
    NEXT_PUBLIC_VAULT_EMAIL=ban@gmail.com
    
    # tùy chọn - cho "npm run db:reset" (chứa mật khẩu DB, giữ bí mật)
    SUPABASE_DB_URL=postgresql://postgres.xxxx:MATKHAU@aws-0-region.pooler.supabase.com:5432/postgres
    Cả 3 biến NEXT_PUBLIC_* đều công khai an toàn - bí mật của bạn được bảo vệ bởi master password, không suy ra được từ chúng.
  3. 3
    Tắt xác nhận email
    Trong Supabase: Authentication → Sign In / Providers → Email, tắt Confirm email. App tạo tài khoản rồi đăng nhập ngay bằng mật khẩu dẫn ra; nếu bật xác nhận thì phiên đầu sẽ bị chặn.
  4. 4
    Tạo schema (bảng + RLS)
    Cách nhanh: mở SQL Editor, dán toàn bộ supabase/migrations/0001_init.sql rồi Run. Hoặc nếu đã đặt SUPABASE_DB_URL:
    npm run db:reset
  5. 5
    Kiểm tra engine (live)
    Chạy smoke-test đầu cuối với tài khoản nháp (không đụng vault thật):
    npm run verify:supabase
    Phải qua đủ 8 PASS: provision, đăng nhập, RLS, mở khóa, recovery key, mã hóa CRUD.
  6. 6
    Chạy local
    npm install
    npm run dev   # http://localhost:3000
    Mở trình duyệt, tạo vault và lưu kỹ 24 từ recovery.
  7. 7
    Deploy lên Vercel
    Đẩy code lên GitHub. Vào Vercel: Add New → Project rồi import repo. Trong phần Environment Variables, thêm đúng 3 biến NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, NEXT_PUBLIC_VAULT_EMAIL. Vercel tự nhận Next.js và build (npm run build đã chạy sẵn CI guard).
Đừng commit .env.local
File .env.local chứa key và đã được gitignore. Chỉ commit .env.local.example. Đặt biến môi trường thật trực tiếp trên Vercel.

Khôi phục, FAQ & Lưu ý

Khôi phục khi quên master password

Mô hình khôi phục dùng file backup mã hóa (Option A): RLS luôn nghiêm ngặt, không có đường đọc dữ liệu từ cloud nếu chưa đăng nhập. File .vault tự chứa mọi thứ cần thiết (đều là ciphertext, an toàn để lưu ở Drive/USB).

  1. 1
    Export định kỳ khi còn nhớ master
    Vào Settings → Backup → Export để tải file .vault. Nên làm định kỳ.
  2. 2
    Khi lỡ quên master
    Tạo vault mới (master mới) trên bất kỳ thiết bị nào.
  3. 3
    Import bằng recovery key
    Vào Settings → Backup → Import, chọn file .vault, tick "dùng recovery key", nhập 24 từ. App giải mã các mục từ file rồi mã hóa lại bằng DEK của vault mới.

Đổi master password

Trong Settings → Account. Thao tác này chỉ bọc lại DEK chứ không mã hóa lại các mục, và recovery key của bạn vẫn còn nguyên hiệu lực.

Mất cả master lẫn recovery key = mất vĩnh viễn
Không có cửa hậu. Hãy in 24 từ recovery ra giấy và cất riêng với file backup. Đây là đánh đổi cố ý của zero-knowledge.

Giới hạn cần biết

Một app chạy trên web tải lại mã nguồn (gồm cả mã mã hóa) mỗi lần mở. Nếu máy chủ hoặc một thư viện phụ thuộc bị xâm nhập, về lý thuyết nó có thể phục vụ mã độc đánh cắp master password lúc bạn gõ. Zero-knowledge không chống được điều này. Giảm thiểu bằng CSP, ghim phiên bản dependency, và tự host.

Mối đe dọa: trong và ngoài tầm bảo vệ

  • Trong tầm (đã giảm thiểu): lộ cơ sở dữ liệu Supabase, nghe lén đường truyền, người trong Supabase, lộ anon key - tất cả chỉ thấy ciphertext.
  • Ngoài tầm: thiết bị của bạn dính keylogger/malware; chuỗi cung ứng web (mã độc phục vụ từ host); mất cả master và recovery key.

Câu hỏi thường gặp

Dùng Gmail thường làm email vault được không?

Được. Email chỉ là định danh đăng nhập và là salt; không có thư nào được gửi (đã tắt Confirm email). Không cần domain riêng.

Vì sao chỉ có 5 loại mục?

Login, Wallet, SSH key, Secure note, API key - đủ bao phủ nhu cầu nêu ra. Seed phrase nằm trong Wallet; cụm từ bảo mật dùng Secure note.

Sắp có gì tiếp theo?

v1.5: mở khóa bằng vân tay/Face (WebAuthn) và bộ tạo mã TOTP. v2: CSP dùng nonce, kiểm tra độ mạnh mật khẩu offline, đóng gói PWA.