Bảo mật
Mã hóa kiểu phong bì (envelope)
Một khóa dữ liệu ngẫu nhiên 256-bit (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 dẫn từ master password (qua Argon2id), một dẫn từ recovery key 24 từ. Đổi master 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)
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-GCMmã hóa từng mục; auth tag tự động từ chối nếu dữ liệu bị sửa hoặc sai khóa.- Recovery key theo chuẩn
BIP39(24 từ tiếng Anh).
Định danh: email làm salt
Mật khẩu đăng nhập Supabase được dẫn từ master + email: authSecret = Argon2id(master, salt = SHA-256(email)). Nhờ vậy tính được ngay mà không cần đọc cơ sở dữ liệu trước. Email không phải biến môi trường: bạn nhập ở lần đăng nhập đầu, app nhớ trong localStorage và điền sẵn lần sau.
Master password + email
▼Argon2id (salt = SHA-256(email))
authSecret → đăng nhập Supabase
▼tải vault_config (ciphertext)
KEK_master → unwrap DEK
▼tải + giải mã trong RAM
Danh sách mục đã giải 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 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.
Ranh giới với máy chủ
- Chỉ ciphertext: Supabase nhận về dữ liệu đã mã hóa; hai cột công khai duy nhất là
typevàfavorite(để đếm + lọ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. - Không plaintext qua server: app chỉ gọi Supabase từ trình duyệt; CI guard chặn import lớp crypto/vault vào ngữ cảnh server.
Mô hình đe dọa (thành thật)
Lộ master password = lộ toàn bộ
Master dẫn ra CẢ authSecret (đăng nhập) lẫn KEK_master (mở khóa DEK), nên ai có master + email (email gần như công khai) là vào được hết. Sinh trắc và 2FA chỉ là tiện ích, KHÔNG phải lớp bảo vệ thứ hai cho vault. Recovery key là một gốc tin cậy thứ hai (lộ master HOẶC recovery key đều dẫn tới toàn bộ).
- Trong tầm (đã giảm thiểu): lộ DB 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ị dính keylogger/malware; chuỗi cung ứng web (mã độc phục vụ từ host - giảm thiểu bằng pin deps, tự host, PWA ghim code); mất cả master và recovery key.