Chapter 12 Security and Permission Management

Haiyue
48min

Chapter 12: Security and Permission Management

Learning Objectives

  1. Master Chrome extension security architecture and threat models
  2. Learn to properly configure and manage permissions
  3. Implement secure data handling and communication mechanisms
  4. Prevent common security attacks and vulnerabilities

12.1 Security Architecture Overview

12.1.1 Chrome Extension Security Model

🔄 正在渲染 Mermaid 图表...

12.1.2 Security Threat Analysis

// security-analyzer.js - Security threat analyzer
class SecurityAnalyzer {
  constructor() {
    this.threats = new Map();
    this.vulnerabilities = new Set();
    this.securityPolicies = new Map();
    this.auditLog = [];
    this.init();
  }

  init() {
    this.setupThreatDetection();
    this.setupVulnerabilityScanning();
    this.setupSecurityPolicies();
    this.startSecurityMonitoring();
  }

  // Threat model definition
  defineThreatModel() {
    return {
      // Data exfiltration threat
      dataExfiltration: {
        description: 'Sensitive data is maliciously accessed or transmitted',
        likelihood: 'medium',
        impact: 'high',
        mitigations: [
          'Data encryption',
          'Access control',
          'Audit logging',
          'Principle of least privilege'
        ]
      },

      // Code injection threat
      codeInjection: {
        description: 'Malicious code is injected into the extension',
        likelihood: 'medium',
        impact: 'critical',
        mitigations: [
          'CSP policy',
          'Input validation',
          'Code review',
          'Sandbox execution'
        ]
      },

      // Privilege escalation threat
      privilegeEscalation: {
        description: 'Extension permissions are misused',
        likelihood: 'low',
        impact: 'high',
        mitigations: [
          'Minimize permissions',
          'Dynamic permissions',
          'User authorization',
          'Permission auditing'
        ]
      },

      // Cross-site scripting attack
      xssAttack: {
        description: 'Malicious scripts execute on web pages',
        likelihood: 'high',
        impact: 'medium',
        mitigations: [
          'Output encoding',
          'Input filtering',
          'CSP protection',
          'Domain validation'
        ]
      },

      // Man-in-the-middle attack
      mitm: {
        description: 'Network communication is intercepted or tampered with',
        likelihood: 'low',
        impact: 'high',
        mitigations: [
          'HTTPS enforcement',
          'Certificate validation',
          'Request signing',
          'Communication encryption'
        ]
      }
    };
  }

  setupThreatDetection() {
    // Detect suspicious network requests
    this.monitorNetworkRequests();

    // Detect abnormal permission usage
    this.monitorPermissionUsage();

    // Detect potential XSS attacks
    this.monitorXSSAttempts();

    // Detect data leakage risks
    this.monitorDataAccess();
  }

  monitorNetworkRequests() {
    // Wrap fetch and XMLHttpRequest
    const originalFetch = window.fetch;
    window.fetch = (...args) => {
      this.analyzeRequest('fetch', args);
      return originalFetch.apply(this, args);
    };

    const originalXHROpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(...args) {
      this.analyzeRequest('xhr', args);
      return originalXHROpen.apply(this, args);
    }.bind(this);
  }

  analyzeRequest(type, args) {
    const url = args[0] || args[1];

    if (typeof url === 'string') {
      // Check for suspicious domains
      if (this.isSuspiciousDomain(url)) {
        this.reportThreat('suspicious_domain', {
          url,
          type,
          timestamp: Date.now()
        });
      }

      // Check if using HTTP (should use HTTPS)
      if (url.startsWith('http://') && !url.includes('localhost')) {
        this.reportThreat('insecure_connection', {
          url,
          type,
          timestamp: Date.now()
        });
      }

      // Check if carrying sensitive data
      if (this.containsSensitiveData(args)) {
        this.reportThreat('sensitive_data_transmission', {
          url,
          type,
          timestamp: Date.now()
        });
      }
    }
  }

  monitorPermissionUsage() {
    // Wrap chrome API calls
    const originalTabsQuery = chrome.tabs.query;
    chrome.tabs.query = (...args) => {
      this.auditAPIUsage('tabs.query', args);
      return originalTabsQuery.apply(chrome.tabs, args);
    };

    const originalStorageGet = chrome.storage.sync.get;
    chrome.storage.sync.get = (...args) => {
      this.auditAPIUsage('storage.sync.get', args);
      return originalStorageGet.apply(chrome.storage.sync, args);
    };
  }

  auditAPIUsage(api, args) {
    const usage = {
      api,
      args: this.sanitizeArgs(args),
      timestamp: Date.now(),
      callStack: new Error().stack
    };

    this.auditLog.push(usage);

    // Check for abnormally frequent API calls
    const recentUsage = this.auditLog.filter(log =>
      log.api === api &&
      Date.now() - log.timestamp < 60000 // Last minute
    );

    if (recentUsage.length > 100) {
      this.reportThreat('excessive_api_usage', {
        api,
        count: recentUsage.length,
        timestamp: Date.now()
      });
    }
  }

  monitorXSSAttempts() {
    // Monitor DOM insertion operations
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList') {
          mutation.addedNodes.forEach(node => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              this.scanForXSS(node);
            }
          });
        }
      });
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }

  scanForXSS(element) {
    const suspiciousPatterns = [
      /<script[^>]*>.*<\/script>/gi,
      /javascript:/gi,
      /on\w+\s*=/gi,
      /<iframe[^>]*>/gi
    ];

    const innerHTML = element.innerHTML;
    suspiciousPatterns.forEach(pattern => {
      if (pattern.test(innerHTML)) {
        this.reportThreat('potential_xss', {
          element: element.tagName,
          content: innerHTML.substring(0, 200),
          timestamp: Date.now()
        });
      }
    });
  }

  monitorDataAccess() {
    // Monitor localStorage access
    const originalSetItem = localStorage.setItem;
    localStorage.setItem = function(key, value) {
      this.auditDataAccess('localStorage.setItem', key, value);
      return originalSetItem.call(localStorage, key, value);
    }.bind(this);

    const originalGetItem = localStorage.getItem;
    localStorage.getItem = function(key) {
      this.auditDataAccess('localStorage.getItem', key);
      return originalGetItem.call(localStorage, key);
    }.bind(this);
  }

  auditDataAccess(operation, key, value = null) {
    // Check if accessing sensitive data
    if (this.isSensitiveKey(key)) {
      this.auditLog.push({
        operation,
        key,
        hasSensitiveValue: value && this.isSensitiveValue(value),
        timestamp: Date.now()
      });
    }
  }

  isSuspiciousDomain(url) {
    const suspiciousDomains = [
      'malicious-site.com',
      'phishing-domain.net',
      'suspicious-tracker.org'
    ];

    try {
      const domain = new URL(url).hostname;
      return suspiciousDomains.some(suspiciousDomain =>
        domain.includes(suspiciousDomain)
      );
    } catch {
      return false;
    }
  }

  containsSensitiveData(requestArgs) {
    const requestBody = JSON.stringify(requestArgs);
    const sensitivePatterns = [
      /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, // Credit card numbers
      /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Email addresses
      /\b\d{11}\b/g, // Phone numbers
      /password|token|secret|key/gi // Sensitive keywords
    ];

    return sensitivePatterns.some(pattern => pattern.test(requestBody));
  }

  isSensitiveKey(key) {
    const sensitiveKeys = [
      'password', 'token', 'secret', 'apiKey', 'privateKey',
      'creditCard', 'ssn', 'personalData', 'authToken'
    ];

    return sensitiveKeys.some(sensitiveKey =>
      key.toLowerCase().includes(sensitiveKey.toLowerCase())
    );
  }

  isSensitiveValue(value) {
    if (typeof value !== 'string') return false;

    // Check if looks like a password
    if (value.length > 8 && /[A-Z]/.test(value) && /[0-9]/.test(value)) {
      return true;
    }

    // Check if looks like a token
    if (value.length > 32 && /^[A-Za-z0-9+/]+=*$/.test(value)) {
      return true;
    }

    return false;
  }

  reportThreat(threatType, details) {
    const threat = {
      type: threatType,
      details,
      severity: this.calculateSeverity(threatType),
      timestamp: Date.now(),
      resolved: false
    };

    this.threats.set(`${threatType}_${Date.now()}`, threat);

    console.warn('Security threat detected:', threat);

    // Send to security monitoring service (if available)
    this.sendSecurityAlert(threat);
  }

  calculateSeverity(threatType) {
    const severityMap = {
      'suspicious_domain': 'high',
      'insecure_connection': 'medium',
      'sensitive_data_transmission': 'high',
      'excessive_api_usage': 'medium',
      'potential_xss': 'high',
      'unauthorized_access': 'critical'
    };

    return severityMap[threatType] || 'low';
  }

  sendSecurityAlert(threat) {
    // This can send to a security monitoring service
    // Or log to local storage
    chrome.runtime.sendMessage({
      action: 'securityAlert',
      threat: threat
    });
  }

  sanitizeArgs(args) {
    // Remove sensitive information
    return args.map(arg => {
      if (typeof arg === 'string' && this.isSensitiveValue(arg)) {
        return '[SENSITIVE_DATA]';
      }
      if (typeof arg === 'object' && arg !== null) {
        const sanitized = {};
        Object.keys(arg).forEach(key => {
          sanitized[key] = this.isSensitiveKey(key) ? '[SENSITIVE_DATA]' : arg[key];
        });
        return sanitized;
      }
      return arg;
    });
  }

  setupVulnerabilityScanning() {
    // Periodically scan for known vulnerabilities
    setInterval(() => {
      this.scanForVulnerabilities();
    }, 300000); // Every 5 minutes
  }

  scanForVulnerabilities() {
    // Check dependency security issues
    this.checkDependencyVulnerabilities();

    // Check configuration security issues
    this.checkConfigurationSecurity();

    // Check permission usage issues
    this.checkPermissionSecurity();
  }

  checkDependencyVulnerabilities() {
    // Check if using libraries with known vulnerabilities
    const knownVulnerableLibraries = [
      'jquery@1.x.x',
      'lodash@4.17.11',
      'axios@0.18.0'
    ];

    // Should check actually loaded libraries
    // Simplified example
    knownVulnerableLibraries.forEach(lib => {
      if (this.isLibraryLoaded(lib)) {
        this.vulnerabilities.add(`vulnerable_dependency: ${lib}`);
      }
    });
  }

  checkConfigurationSecurity() {
    // Check CSP configuration
    const csp = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
    if (!csp || !this.isSecureCSP(csp.content)) {
      this.vulnerabilities.add('weak_csp');
    }

    // Check if using HTTPS
    if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
      this.vulnerabilities.add('insecure_protocol');
    }
  }

  checkPermissionSecurity() {
    // Check if using excessive permissions
    chrome.management.getSelf((extensionInfo) => {
      const permissions = extensionInfo.permissions;
      const excessivePermissions = [
        'tabs', 'history', 'bookmarks'
      ];

      const hasExcessive = permissions.some(permission =>
        excessivePermissions.includes(permission)
      );

      if (hasExcessive) {
        this.vulnerabilities.add('excessive_permissions');
      }
    });
  }

  isLibraryLoaded(libraryPattern) {
    // Simplified check, should be more complex in practice
    return false;
  }

  isSecureCSP(cspContent) {
    if (!cspContent) return false;

    // Check if unsafe inline scripts are prohibited
    if (cspContent.includes("'unsafe-inline'")) return false;

    // Check if eval is prohibited
    if (cspContent.includes("'unsafe-eval'")) return false;

    // Check if there are appropriate source restrictions
    if (!cspContent.includes('self') && !cspContent.includes('none')) return false;

    return true;
  }

  setupSecurityPolicies() {
    this.securityPolicies.set('dataRetention', {
      maxAge: 90 * 24 * 60 * 60 * 1000, // 90 days
      sensitiveDataMaxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
      cleanupInterval: 24 * 60 * 60 * 1000 // Daily cleanup
    });

    this.securityPolicies.set('networkSecurity', {
      allowedDomains: ['*.example.com', 'api.service.com'],
      requireHTTPS: true,
      maxRequestSize: 1024 * 1024, // 1MB
      timeoutMs: 30000
    });

    this.securityPolicies.set('accessControl', {
      maxLoginAttempts: 3,
      lockoutDurationMs: 15 * 60 * 1000, // 15 minutes
      sessionTimeoutMs: 2 * 60 * 60 * 1000, // 2 hours
      requireReauthentication: true
    });
  }

  startSecurityMonitoring() {
    // Periodically clean sensitive data
    setInterval(() => {
      this.cleanupSensitiveData();
    }, 24 * 60 * 60 * 1000); // Daily

    // Monitor abnormal behavior
    setInterval(() => {
      this.analyzeUserBehavior();
    }, 60 * 1000); // Every minute
  }

  cleanupSensitiveData() {
    const policy = this.securityPolicies.get('dataRetention');
    const now = Date.now();

    // Clean expired audit logs
    this.auditLog = this.auditLog.filter(log =>
      now - log.timestamp < policy.maxAge
    );

    // Clean expired threat records
    for (const [key, threat] of this.threats.entries()) {
      if (now - threat.timestamp > policy.maxAge) {
        this.threats.delete(key);
      }
    }
  }

  analyzeUserBehavior() {
    // Analyze recent activity patterns
    const recentActivity = this.auditLog.filter(log =>
      Date.now() - log.timestamp < 5 * 60 * 1000 // Last 5 minutes
    );

    // Check for abnormal activity
    if (recentActivity.length > 50) {
      this.reportThreat('unusual_activity', {
        activityCount: recentActivity.length,
        timeWindow: '5 minutes'
      });
    }

    // Check permission usage patterns
    const apiUsage = {};
    recentActivity.forEach(log => {
      if (log.api) {
        apiUsage[log.api] = (apiUsage[log.api] || 0) + 1;
      }
    });

    Object.entries(apiUsage).forEach(([api, count]) => {
      if (count > 20) {
        this.reportThreat('excessive_api_usage', {
          api,
          count,
          timeWindow: '5 minutes'
        });
      }
    });
  }

  // Get security report
  getSecurityReport() {
    const now = Date.now();
    const oneDayAgo = now - 24 * 60 * 60 * 1000;

    const recentThreats = Array.from(this.threats.values())
      .filter(threat => threat.timestamp > oneDayAgo);

    const threatsByType = {};
    recentThreats.forEach(threat => {
      threatsByType[threat.type] = (threatsByType[threat.type] || 0) + 1;
    });

    const criticalThreats = recentThreats.filter(threat =>
      threat.severity === 'critical'
    );

    const highThreats = recentThreats.filter(threat =>
      threat.severity === 'high'
    );

    return {
      summary: {
        totalThreats: recentThreats.length,
        criticalThreats: criticalThreats.length,
        highThreats: highThreats.length,
        vulnerabilities: Array.from(this.vulnerabilities),
        securityScore: this.calculateSecurityScore()
      },
      threatsByType,
      recentThreats: recentThreats.slice(0, 10), // Last 10 threats
      auditStats: {
        totalEvents: this.auditLog.length,
        recentEvents: this.auditLog.filter(log =>
          log.timestamp > oneDayAgo
        ).length
      }
    };
  }

  calculateSecurityScore() {
    let score = 100;

    // Deduct points based on threat count
    const threats = Array.from(this.threats.values());
    const criticalThreats = threats.filter(t => t.severity === 'critical');
    const highThreats = threats.filter(t => t.severity === 'high');
    const mediumThreats = threats.filter(t => t.severity === 'medium');

    score -= criticalThreats.length * 25;
    score -= highThreats.length * 10;
    score -= mediumThreats.length * 5;

    // Deduct points based on vulnerability count
    score -= this.vulnerabilities.size * 15;

    return Math.max(0, Math.min(100, score));
  }
}

// Global security analyzer instance
const securityAnalyzer = new SecurityAnalyzer();

// Export for use by other modules
window.securityAnalyzer = securityAnalyzer;

12.2 Permission Management

12.2.1 Dynamic Permission System

// permission-manager.js - Permission manager
class PermissionManager {
  constructor() {
    this.requiredPermissions = new Set();
    this.optionalPermissions = new Set();
    this.grantedPermissions = new Set();
    this.permissionRequests = new Map();
    this.permissionUsage = new Map();
    this.init();
  }

  async init() {
    await this.loadCurrentPermissions();
    this.setupPermissionMonitoring();
    this.setupPermissionPolicies();
  }

  async loadCurrentPermissions() {
    return new Promise((resolve) => {
      chrome.permissions.getAll((permissions) => {
        permissions.permissions.forEach(permission => {
          this.grantedPermissions.add(permission);
        });

        permissions.origins.forEach(origin => {
          this.grantedPermissions.add(origin);
        });

        console.log('Currently granted permissions:', Array.from(this.grantedPermissions));
        resolve();
      });
    });
  }

  // Permission classification management
  definePermissions() {
    // Required permissions (declared in manifest)
    this.requiredPermissions = new Set([
      'storage',
      'activeTab'
    ]);

    // Optional permissions (requested on demand)
    this.optionalPermissions = new Set([
      'tabs',
      'history',
      'bookmarks',
      'downloads',
      'notifications',
      'contextMenus',
      'https://*.example.com/*',
      'https://api.service.com/*'
    ]);
  }

  // Request permission
  async requestPermission(permission, justification = '') {
    if (this.grantedPermissions.has(permission)) {
      return { granted: true, alreadyGranted: true };
    }

    if (!this.optionalPermissions.has(permission)) {
      return {
        granted: false,
        error: 'Permission is not in the optional permissions list'
      };
    }

    const requestId = this.generateRequestId();
    const request = {
      id: requestId,
      permission,
      justification,
      timestamp: Date.now(),
      status: 'pending'
    };

    this.permissionRequests.set(requestId, request);

    try {
      // Show permission request dialog
      const userApproved = await this.showPermissionDialog(permission, justification);

      if (!userApproved) {
        request.status = 'denied';
        return { granted: false, userDenied: true };
      }

      // Request Chrome permission
      const granted = await this.requestChromePermission(permission);

      if (granted) {
        this.grantedPermissions.add(permission);
        request.status = 'granted';
        this.logPermissionUsage(permission, 'granted');

        return { granted: true, newlyGranted: true };
      } else {
        request.status = 'denied';
        return { granted: false, chromeDenied: true };
      }

    } catch (error) {
      request.status = 'error';
      request.error = error.message;
      return { granted: false, error: error.message };
    }
  }

  async showPermissionDialog(permission, justification) {
    return new Promise((resolve) => {
      const modal = this.createPermissionModal(permission, justification);

      const approveBtn = modal.querySelector('.approve-btn');
      const denyBtn = modal.querySelector('.deny-btn');

      approveBtn.addEventListener('click', () => {
        document.body.removeChild(modal);
        resolve(true);
      });

      denyBtn.addEventListener('click', () => {
        document.body.removeChild(modal);
        resolve(false);
      });

      document.body.appendChild(modal);

      // Auto-focus on deny button (safer default choice)
      denyBtn.focus();
    });
  }

  createPermissionModal(permission, justification) {
    const modal = document.createElement('div');
    modal.className = 'permission-modal-overlay';

    const permissionInfo = this.getPermissionInfo(permission);

    modal.innerHTML = `
      <div class="permission-modal">
        <div class="modal-header">
          <h3>Permission Request</h3>
          <span class="security-badge">${permissionInfo.riskLevel}</span>
        </div>

        <div class="modal-body">
          <div class="permission-icon">
            <span>${permissionInfo.icon}</span>
          </div>

          <h4>${permissionInfo.name}</h4>
          <p class="permission-description">${permissionInfo.description}</p>

          ${justification ? `
            <div class="justification">
              <h5>Why this permission is needed:</h5>
              <p>${justification}</p>
            </div>
          ` : ''}

          <div class="risk-info">
            <h5>Risk Warning:</h5>
            <ul>
              ${permissionInfo.risks.map(risk => `<li>${risk}</li>`).join('')}
            </ul>
          </div>

          <div class="usage-info">
            <h5>This permission will be used for:</h5>
            <ul>
              ${permissionInfo.usages.map(usage => `<li>${usage}</li>`).join('')}
            </ul>
          </div>
        </div>

        <div class="modal-footer">
          <button class="btn deny-btn">Deny</button>
          <button class="btn approve-btn">Grant</button>
        </div>
      </div>
    `;

    return modal;
  }

  getPermissionInfo(permission) {
    const permissionMap = {
      'tabs': {
        name: 'Tab Access',
        icon: '🗂️',
        riskLevel: 'high',
        description: 'Allows the extension to view and control browser tabs',
        risks: [
          'Can access all tab URLs and titles',
          'Can close or modify tabs',
          'May leak browsing history'
        ],
        usages: [
          'Manage tabs',
          'Sync tab states',
          'Provide tab-related features'
        ]
      },

      'history': {
        name: 'Browsing History',
        icon: '📚',
        riskLevel: 'high',
        description: 'Allows the extension to access browsing history',
        risks: [
          'Can view complete browsing history',
          'May leak privacy information',
          'Can delete history records'
        ],
        usages: [
          'Analyze browsing habits',
          'Provide history search',
          'Generate statistics reports'
        ]
      },

      'bookmarks': {
        name: 'Bookmark Management',
        icon: '🔖',
        riskLevel: 'medium',
        description: 'Allows the extension to access and manage bookmarks',
        risks: [
          'Can view all bookmarks',
          'Can modify or delete bookmarks',
          'May leak personal preferences'
        ],
        usages: [
          'Sync bookmarks',
          'Manage bookmarks',
          'Import/export bookmarks'
        ]
      },

      'downloads': {
        name: 'Download Management',
        icon: '💾',
        riskLevel: 'medium',
        description: 'Allows the extension to manage download items',
        risks: [
          'Can view download history',
          'Can control download behavior',
          'Can access downloaded files'
        ],
        usages: [
          'Manage download queue',
          'Provide download enhancements',
          'Monitor download progress'
        ]
      },

      'notifications': {
        name: 'Desktop Notifications',
        icon: '🔔',
        riskLevel: 'low',
        description: 'Allows the extension to display desktop notifications',
        risks: [
          'May display disruptive notifications',
          'May be used for spam'
        ],
        usages: [
          'Important event reminders',
          'Status update notifications',
          'User interaction feedback'
        ]
      }
    };

    return permissionMap[permission] || {
      name: permission,
      icon: '⚠️',
      riskLevel: 'unknown',
      description: `${permission} permission`,
      risks: ['Unknown risk'],
      usages: ['Unknown usage']
    };
  }

  async requestChromePermission(permission) {
    return new Promise((resolve) => {
      const permissionObject = {
        permissions: [],
        origins: []
      };

      if (permission.includes('://')) {
        permissionObject.origins.push(permission);
      } else {
        permissionObject.permissions.push(permission);
      }

      chrome.permissions.request(permissionObject, (granted) => {
        resolve(granted);
      });
    });
  }

  // Revoke permission
  async revokePermission(permission) {
    if (!this.grantedPermissions.has(permission)) {
      return { revoked: false, notGranted: true };
    }

    try {
      const permissionObject = {
        permissions: [],
        origins: []
      };

      if (permission.includes('://')) {
        permissionObject.origins.push(permission);
      } else {
        permissionObject.permissions.push(permission);
      }

      const revoked = await new Promise((resolve) => {
        chrome.permissions.remove(permissionObject, resolve);
      });

      if (revoked) {
        this.grantedPermissions.delete(permission);
        this.logPermissionUsage(permission, 'revoked');
        return { revoked: true };
      } else {
        return { revoked: false, error: 'Chrome permission revocation failed' };
      }

    } catch (error) {
      return { revoked: false, error: error.message };
    }
  }

  // Check permission
  hasPermission(permission) {
    return this.grantedPermissions.has(permission);
  }

  async checkPermission(permission) {
    return new Promise((resolve) => {
      const permissionObject = {
        permissions: [],
        origins: []
      };

      if (permission.includes('://')) {
        permissionObject.origins.push(permission);
      } else {
        permissionObject.permissions.push(permission);
      }

      chrome.permissions.contains(permissionObject, resolve);
    });
  }

  // Batch permission management
  async requestMultiplePermissions(permissions, justifications = {}) {
    const results = {};

    for (const permission of permissions) {
      const justification = justifications[permission] || '';
      results[permission] = await this.requestPermission(permission, justification);

      // If user denied permission, ask if should continue
      if (!results[permission].granted && results[permission].userDenied) {
        const shouldContinue = confirm('Some permissions were denied. Continue requesting other permissions?');
        if (!shouldContinue) break;
      }
    }

    return results;
  }

  // Permission usage monitoring
  setupPermissionMonitoring() {
    chrome.permissions.onAdded.addListener((permissions) => {
      permissions.permissions.forEach(permission => {
        this.grantedPermissions.add(permission);
        console.log('Permission added:', permission);
      });

      permissions.origins.forEach(origin => {
        this.grantedPermissions.add(origin);
        console.log('Permission added:', origin);
      });
    });

    chrome.permissions.onRemoved.addListener((permissions) => {
      permissions.permissions.forEach(permission => {
        this.grantedPermissions.delete(permission);
        console.log('Permission removed:', permission);
      });

      permissions.origins.forEach(origin => {
        this.grantedPermissions.delete(origin);
        console.log('Permission removed:', origin);
      });
    });
  }

  logPermissionUsage(permission, action, details = {}) {
    const usage = {
      permission,
      action,
      details,
      timestamp: Date.now()
    };

    if (!this.permissionUsage.has(permission)) {
      this.permissionUsage.set(permission, []);
    }

    this.permissionUsage.get(permission).push(usage);

    // Limit log size
    const logs = this.permissionUsage.get(permission);
    if (logs.length > 100) {
      logs.splice(0, logs.length - 50);
    }
  }

  setupPermissionPolicies() {
    // Set permission usage policies
    this.policies = {
      // Auto-revoke policy
      autoRevoke: {
        enabled: true,
        unusedDays: 30, // Auto-revoke after 30 days of non-use
        excludePermissions: ['storage', 'activeTab'] // Exclude basic permissions
      },

      // Permission usage warning policy
      usageWarning: {
        enabled: true,
        excessiveUsageThreshold: 100, // More than 100 uses in one hour
        warningCooldown: 24 * 60 * 60 * 1000 // Don't repeat warning within 24 hours
      },

      // Sensitive permission policy
      sensitivePermissions: {
        permissions: ['history', 'bookmarks', 'downloads'],
        requireReconfirmation: true,
        reconfirmationInterval: 7 * 24 * 60 * 60 * 1000 // Reconfirm every 7 days
      }
    };

    // Start policy enforcement
    this.startPolicyEnforcement();
  }

  startPolicyEnforcement() {
    // Check permission usage daily
    setInterval(() => {
      this.enforcePermissionPolicies();
    }, 24 * 60 * 60 * 1000);

    // Check usage patterns hourly
    setInterval(() => {
      this.checkUsagePatterns();
    }, 60 * 60 * 1000);
  }

  enforcePermissionPolicies() {
    if (this.policies.autoRevoke.enabled) {
      this.revokeUnusedPermissions();
    }

    if (this.policies.sensitivePermissions.requireReconfirmation) {
      this.checkSensitivePermissionReconfirmation();
    }
  }

  revokeUnusedPermissions() {
    const cutoffTime = Date.now() - (this.policies.autoRevoke.unusedDays * 24 * 60 * 60 * 1000);

    this.grantedPermissions.forEach(permission => {
      if (this.policies.autoRevoke.excludePermissions.includes(permission)) {
        return;
      }

      const usageLogs = this.permissionUsage.get(permission) || [];
      const recentUsage = usageLogs.filter(log =>
        log.timestamp > cutoffTime && log.action === 'used'
      );

      if (recentUsage.length === 0) {
        console.log(`Permission ${permission} has not been used for ${this.policies.autoRevoke.unusedDays} days, consider revoking`);

        // Can choose to auto-revoke or notify user here
        this.promptPermissionRevoke(permission);
      }
    });
  }

  promptPermissionRevoke(permission) {
    const shouldRevoke = confirm(
      `Permission "${permission}" has not been used for a long time. Revoke this permission?\n\n` +
      'You can request it again when needed.'
    );

    if (shouldRevoke) {
      this.revokePermission(permission);
    }
  }

  checkUsagePatterns() {
    const oneHourAgo = Date.now() - 60 * 60 * 1000;

    this.permissionUsage.forEach((logs, permission) => {
      const recentUsage = logs.filter(log =>
        log.timestamp > oneHourAgo && log.action === 'used'
      );

      if (recentUsage.length > this.policies.usageWarning.excessiveUsageThreshold) {
        this.warnExcessiveUsage(permission, recentUsage.length);
      }
    });
  }

  warnExcessiveUsage(permission, count) {
    console.warn(`Permission ${permission} was used ${count} times in the past hour, potential anomaly`);

    // Report to security analyzer
    if (window.securityAnalyzer) {
      window.securityAnalyzer.reportThreat('excessive_permission_usage', {
        permission,
        count,
        timeWindow: '1 hour'
      });
    }
  }

  generateRequestId() {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  // Get permission report
  getPermissionReport() {
    const totalPermissions = this.grantedPermissions.size;
    const requiredCount = Array.from(this.grantedPermissions).filter(p =>
      this.requiredPermissions.has(p)
    ).length;
    const optionalCount = totalPermissions - requiredCount;

    const usageStats = {};
    this.permissionUsage.forEach((logs, permission) => {
      usageStats[permission] = {
        totalUsage: logs.length,
        recentUsage: logs.filter(log =>
          Date.now() - log.timestamp < 24 * 60 * 60 * 1000
        ).length,
        lastUsed: logs.length > 0 ? Math.max(...logs.map(log => log.timestamp)) : null
      };
    });

    return {
      summary: {
        totalPermissions,
        requiredPermissions: requiredCount,
        optionalPermissions: optionalCount,
        pendingRequests: Array.from(this.permissionRequests.values()).filter(req =>
          req.status === 'pending'
        ).length
      },
      permissions: Array.from(this.grantedPermissions),
      usageStats,
      recentRequests: Array.from(this.permissionRequests.values())
        .filter(req => Date.now() - req.timestamp < 24 * 60 * 60 * 1000)
        .sort((a, b) => b.timestamp - a.timestamp)
    };
  }
}

// Global permission manager instance
const permissionManager = new PermissionManager();

// Export for use by other modules
window.permissionManager = permissionManager;

12.3 Data Encryption and Protection

12.3.1 Data Encryption Implementation

// crypto-manager.js - Encryption manager
class CryptoManager {
  constructor() {
    this.keyCache = new Map();
    this.encryptionAlgorithm = 'AES-GCM';
    this.keyDerivationAlgorithm = 'PBKDF2';
    this.hashAlgorithm = 'SHA-256';
    this.init();
  }

  async init() {
    // Check browser encryption support
    if (!window.crypto || !window.crypto.subtle) {
      throw new Error('Browser does not support Web Crypto API');
    }

    console.log('Encryption manager initialized');
  }

  // Generate random key
  async generateKey(algorithm = this.encryptionAlgorithm) {
    try {
      const key = await crypto.subtle.generateKey(
        {
          name: algorithm,
          length: 256
        },
        true, // Extractable
        ['encrypt', 'decrypt']
      );

      return key;
    } catch (error) {
      console.error('Key generation failed:', error);
      throw error;
    }
  }

  // Derive key from password
  async deriveKeyFromPassword(password, salt = null) {
    // If no salt provided, generate a new one
    if (!salt) {
      salt = crypto.getRandomValues(new Uint8Array(16));
    }

    const encoder = new TextEncoder();
    const passwordBuffer = encoder.encode(password);

    // Import password as key material
    const keyMaterial = await crypto.subtle.importKey(
      'raw',
      passwordBuffer,
      { name: this.keyDerivationAlgorithm },
      false,
      ['deriveKey']
    );

    // Derive AES key
    const derivedKey = await crypto.subtle.deriveKey(
      {
        name: this.keyDerivationAlgorithm,
        salt: salt,
        iterations: 100000, // OWASP recommended minimum
        hash: this.hashAlgorithm
      },
      keyMaterial,
      {
        name: this.encryptionAlgorithm,
        length: 256
      },
      false, // Not extractable (more secure)
      ['encrypt', 'decrypt']
    );

    return {
      key: derivedKey,
      salt: salt
    };
  }

  // Encrypt data
  async encryptData(data, key = null) {
    try {
      // If no key provided, generate a new one
      if (!key) {
        key = await this.generateKey();
      }

      // Generate random IV
      const iv = crypto.getRandomValues(new Uint8Array(12));

      // Convert data to ArrayBuffer
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(JSON.stringify(data));

      // Perform encryption
      const encrypted = await crypto.subtle.encrypt(
        {
          name: this.encryptionAlgorithm,
          iv: iv
        },
        key,
        dataBuffer
      );

      // Export key (if extractable)
      let exportedKey = null;
      try {
        exportedKey = await crypto.subtle.exportKey('jwk', key);
      } catch (e) {
        // Key is not extractable, this is normal
      }

      return {
        encrypted: Array.from(new Uint8Array(encrypted)),
        iv: Array.from(iv),
        key: exportedKey,
        algorithm: this.encryptionAlgorithm
      };

    } catch (error) {
      console.error('Data encryption failed:', error);
      throw error;
    }
  }

  // Decrypt data
  async decryptData(encryptedData, key = null) {
    try {
      let cryptoKey = key;

      // If provided key is exported, need to import it
      if (key && typeof key === 'object' && key.kty) {
        cryptoKey = await crypto.subtle.importKey(
          'jwk',
          key,
          { name: this.encryptionAlgorithm },
          false,
          ['decrypt']
        );
      }

      if (!cryptoKey) {
        throw new Error('Decryption key is not available');
      }

      // Convert data format
      const encrypted = new Uint8Array(encryptedData.encrypted);
      const iv = new Uint8Array(encryptedData.iv);

      // Perform decryption
      const decrypted = await crypto.subtle.decrypt(
        {
          name: encryptedData.algorithm || this.encryptionAlgorithm,
          iv: iv
        },
        cryptoKey,
        encrypted
      );

      // Convert back to original data
      const decoder = new TextDecoder();
      const decryptedString = decoder.decode(decrypted);

      return JSON.parse(decryptedString);

    } catch (error) {
      console.error('Data decryption failed:', error);
      throw error;
    }
  }

  // Calculate data hash
  async hashData(data, algorithm = this.hashAlgorithm) {
    try {
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(JSON.stringify(data));

      const hashBuffer = await crypto.subtle.digest(algorithm, dataBuffer);
      const hashArray = Array.from(new Uint8Array(hashBuffer));

      return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');

    } catch (error) {
      console.error('Data hashing failed:', error);
      throw error;
    }
  }

  // Generate HMAC
  async generateHMAC(data, secret) {
    try {
      const encoder = new TextEncoder();

      // Import key
      const key = await crypto.subtle.importKey(
        'raw',
        encoder.encode(secret),
        { name: 'HMAC', hash: this.hashAlgorithm },
        false,
        ['sign']
      );

      // Generate HMAC
      const signature = await crypto.subtle.sign(
        'HMAC',
        key,
        encoder.encode(JSON.stringify(data))
      );

      const signatureArray = Array.from(new Uint8Array(signature));
      return signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');

    } catch (error) {
      console.error('HMAC generation failed:', error);
      throw error;
    }
  }

  // Verify HMAC
  async verifyHMAC(data, signature, secret) {
    try {
      const expectedSignature = await this.generateHMAC(data, secret);
      return expectedSignature === signature;

    } catch (error) {
      console.error('HMAC verification failed:', error);
      return false;
    }
  }

  // Secure data storage
  async secureStore(key, data, password = null) {
    try {
      let encryptionKey;

      if (password) {
        // Derive key from password
        const derivedKeyInfo = await this.deriveKeyFromPassword(password);
        encryptionKey = derivedKeyInfo.key;

        // Store salt for later use
        const saltKey = `${key}_salt`;
        await chrome.storage.local.set({
          [saltKey]: Array.from(derivedKeyInfo.salt)
        });
      } else {
        // Generate random key and store it
        encryptionKey = await this.generateKey();
        const exportedKey = await crypto.subtle.exportKey('jwk', encryptionKey);

        const keyStorageKey = `${key}_key`;
        await chrome.storage.local.set({
          [keyStorageKey]: exportedKey
        });
      }

      // Encrypt data
      const encryptedData = await this.encryptData(data, encryptionKey);

      // Store encrypted data
      await chrome.storage.local.set({
        [key]: {
          encrypted: encryptedData.encrypted,
          iv: encryptedData.iv,
          algorithm: encryptedData.algorithm,
          timestamp: Date.now(),
          protected: !!password
        }
      });

      console.log(`Data securely stored: ${key}`);
      return true;

    } catch (error) {
      console.error('Secure storage failed:', error);
      throw error;
    }
  }

  // Securely retrieve data
  async secureRetrieve(key, password = null) {
    try {
      // Get encrypted data
      const result = await chrome.storage.local.get(key);
      const encryptedData = result[key];

      if (!encryptedData) {
        return null;
      }

      let decryptionKey;

      if (encryptedData.protected && password) {
        // Get salt and re-derive key
        const saltResult = await chrome.storage.local.get(`${key}_salt`);
        const salt = new Uint8Array(saltResult[`${key}_salt`]);

        const derivedKeyInfo = await this.deriveKeyFromPassword(password, salt);
        decryptionKey = derivedKeyInfo.key;

      } else if (!encryptedData.protected) {
        // Get stored key
        const keyResult = await chrome.storage.local.get(`${key}_key`);
        const exportedKey = keyResult[`${key}_key`];

        if (exportedKey) {
          decryptionKey = await crypto.subtle.importKey(
            'jwk',
            exportedKey,
            { name: this.encryptionAlgorithm },
            false,
            ['decrypt']
          );
        }
      } else {
        throw new Error('Password required to decrypt protected data');
      }

      if (!decryptionKey) {
        throw new Error('Decryption key is not available');
      }

      // Decrypt data
      const decryptedData = await this.decryptData(encryptedData, decryptionKey);

      console.log(`Data securely retrieved: ${key}`);
      return decryptedData;

    } catch (error) {
      console.error('Secure retrieval failed:', error);
      throw error;
    }
  }

  // Securely delete data
  async secureDelete(key) {
    try {
      const keysToRemove = [
        key,
        `${key}_key`,
        `${key}_salt`
      ];

      await chrome.storage.local.remove(keysToRemove);

      // Clear key cache
      this.keyCache.delete(key);

      console.log(`Secure data deleted: ${key}`);
      return true;

    } catch (error) {
      console.error('Secure deletion failed:', error);
      throw error;
    }
  }

  // Generate secure random numbers
  generateSecureRandom(length = 32) {
    return Array.from(crypto.getRandomValues(new Uint8Array(length)));
  }

  // Generate secure random string
  generateSecureRandomString(length = 32) {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const randomBytes = crypto.getRandomValues(new Uint8Array(length));

    return Array.from(randomBytes)
      .map(byte => chars[byte % chars.length])
      .join('');
  }

  // Password strength check
  checkPasswordStrength(password) {
    const checks = {
      length: password.length >= 12,
      uppercase: /[A-Z]/.test(password),
      lowercase: /[a-z]/.test(password),
      numbers: /[0-9]/.test(password),
      symbols: /[^A-Za-z0-9]/.test(password),
      noCommonWords: !this.containsCommonWords(password),
      noRepeatedChars: !this.hasRepeatedChars(password)
    };

    const score = Object.values(checks).filter(Boolean).length;
    const maxScore = Object.keys(checks).length;

    let strength;
    if (score < 3) strength = 'weak';
    else if (score < 5) strength = 'medium';
    else if (score < 7) strength = 'strong';
    else strength = 'very_strong';

    return {
      strength,
      score,
      maxScore,
      checks,
      suggestions: this.getPasswordSuggestions(checks)
    };
  }

  containsCommonWords(password) {
    const commonWords = [
      'password', '123456', 'qwerty', 'admin', 'welcome',
      'letmein', 'monkey', 'dragon', 'master', 'login'
    ];

    return commonWords.some(word =>
      password.toLowerCase().includes(word)
    );
  }

  hasRepeatedChars(password) {
    return /(.)\1{2,}/.test(password); // Check for 3 or more consecutive identical characters
  }

  getPasswordSuggestions(checks) {
    const suggestions = [];

    if (!checks.length) suggestions.push('Use at least 12 characters');
    if (!checks.uppercase) suggestions.push('Include uppercase letters');
    if (!checks.lowercase) suggestions.push('Include lowercase letters');
    if (!checks.numbers) suggestions.push('Include numbers');
    if (!checks.symbols) suggestions.push('Include special symbols');
    if (!checks.noCommonWords) suggestions.push('Avoid common words');
    if (!checks.noRepeatedChars) suggestions.push('Avoid repeated characters');

    return suggestions;
  }

  // Data integrity verification
  async createIntegrityChecksum(data) {
    const hash = await this.hashData(data);
    const timestamp = Date.now();

    return {
      checksum: hash,
      timestamp,
      algorithm: this.hashAlgorithm
    };
  }

  async verifyIntegrity(data, integrity) {
    try {
      const currentHash = await this.hashData(data);
      return currentHash === integrity.checksum;
    } catch (error) {
      console.error('Integrity verification failed:', error);
      return false;
    }
  }

  // Get encryption status report
  getCryptoReport() {
    const supportedAlgorithms = {
      encryption: [this.encryptionAlgorithm],
      hashing: [this.hashAlgorithm],
      keyDerivation: [this.keyDerivationAlgorithm]
    };

    const securityFeatures = {
      webCryptoSupported: !!window.crypto.subtle,
      randomGenerationSupported: !!window.crypto.getRandomValues,
      keyStorage: 'chrome.storage.local',
      passwordProtection: true,
      integrityChecking: true
    };

    return {
      supportedAlgorithms,
      securityFeatures,
      keyCache: {
        size: this.keyCache.size,
        keys: Array.from(this.keyCache.keys())
      }
    };
  }
}

// Global encryption manager instance
const cryptoManager = new CryptoManager();

// Export for use by other modules
window.cryptoManager = cryptoManager;

warning Security Precautions

  1. Minimize Permissions: Only request necessary permissions, avoid excessive permissions
  2. Data Encryption: Sensitive data must be encrypted for storage
  3. Input Validation: Strictly validate all user inputs
  4. HTTPS Enforcement: All network communications must use HTTPS
  5. Regular Audits: Regularly check permission usage and security configuration

tip Best Practices

  • Implement multiple layers of defense
  • Use strong passwords and multi-factor authentication
  • Regularly update security policies and dependencies
  • Establish security incident response processes
  • Conduct security testing and code reviews

note Summary This chapter provided an in-depth introduction to Chrome extension security architecture, permission management, and data protection techniques. Security is one of the most important considerations in extension development, directly impacting user trust and extension success. In the next chapter, we will learn testing and debugging techniques to ensure extension quality and stability.

Categories