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

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)
authSecret (mật khẩu đăng nhập Supabase) 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; 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ã
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 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à type 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.