HiHuo
首页
博客
手册
工具
首页
博客
手册
工具
  • Vue & CSS 进阶

    • 第0章:工具链与开发体验
    • 第1章:TypeScript 核心
    • 第2章:CSS 现代能力
    • 第3章:Vue3 核心与原理
    • 第4章:路由与状态管理
    • 第5章:工程化与性能
    • 第6章:测试与质量
    • 第7章:CSS 进阶专题
    • 第8章:项目实战A - SaaS仪表盘
    • 第9章:项目实战B - 内容社区
    • 第10章:Vue 内核深入
    • 第11章:微前端与部署
    • CSS 变量体系与主题系统深度指南
    • 前端安全与防护专题
    • CSS 专题:动效优化与微交互
    • CSS 专题:图片与图形处理
    • 实时通信与WebSocket专题
    • 开发工具与调试技巧专题
    • 新兴技术与未来趋势专题
    • 移动端开发与PWA专题
    • 附录A:代码片段库
    • 附录B:练习题集

前端安全与防护专题

掌握前端安全防护技术,构建安全可靠的前端应用

📚 专题目标

通过本专题学习,你将掌握:

  • XSS 攻击防护与内容安全策略
  • CSRF 攻击防护与同源策略
  • 点击劫持防护与安全头设置
  • 认证授权与JWT安全实践
  • 数据加密与敏感信息保护

🛡️ XSS 攻击防护

XSS 攻击类型与防护

// xss-protection.ts
export class XSSProtection {
  private static instance: XSSProtection
  private allowedTags: Set<string> = new Set(['p', 'br', 'strong', 'em', 'span'])
  private allowedAttributes: Set<string> = new Set(['class', 'id'])

  static getInstance(): XSSProtection {
    if (!XSSProtection.instance) {
      XSSProtection.instance = new XSSProtection()
    }
    return XSSProtection.instance
  }

  // HTML 转义
  escapeHtml(text: string): string {
    const div = document.createElement('div')
    div.textContent = text
    return div.innerHTML
  }

  // 属性值转义
  escapeAttribute(value: string): string {
    return value
      .replace(/&/g, '&amp;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#x27;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
  }

  // URL 转义
  escapeUrl(url: string): string {
    try {
      const urlObj = new URL(url)
      // 只允许 http 和 https 协议
      if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
        throw new Error('Invalid protocol')
      }
      return urlObj.toString()
    } catch {
      return '#'
    }
  }

  // 清理 HTML 内容
  sanitizeHtml(html: string): string {
    const parser = new DOMParser()
    const doc = parser.parseFromString(html, 'text/html')
    
    const walker = document.createTreeWalker(
      doc.body,
      NodeFilter.SHOW_ELEMENT,
      null
    )

    const elementsToRemove: Element[] = []
    let node: Node | null

    while (node = walker.nextNode()) {
      const element = node as Element
      
      // 检查标签是否允许
      if (!this.allowedTags.has(element.tagName.toLowerCase())) {
        elementsToRemove.push(element)
        continue
      }

      // 检查属性
      const attributes = Array.from(element.attributes)
      attributes.forEach(attr => {
        if (!this.allowedAttributes.has(attr.name.toLowerCase())) {
          element.removeAttribute(attr.name)
        } else {
          // 转义属性值
          element.setAttribute(attr.name, this.escapeAttribute(attr.value))
        }
      })

      // 检查事件处理器
      const eventHandlers = ['onclick', 'onload', 'onerror', 'onmouseover']
      eventHandlers.forEach(handler => {
        if (element.hasAttribute(handler)) {
          element.removeAttribute(handler)
        }
      })
    }

    // 移除不允许的元素
    elementsToRemove.forEach(element => {
      element.parentNode?.removeChild(element)
    })

    return doc.body.innerHTML
  }

  // 验证用户输入
  validateInput(input: string, type: 'text' | 'email' | 'url' | 'number'): boolean {
    switch (type) {
      case 'email':
        return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input)
      case 'url':
        try {
          new URL(input)
          return true
        } catch {
          return false
        }
      case 'number':
        return !isNaN(Number(input)) && isFinite(Number(input))
      case 'text':
        return input.length > 0 && input.length < 1000
      default:
        return false
    }
  }

  // 检测潜在的 XSS 攻击
  detectXSS(input: string): boolean {
    const xssPatterns = [
      /<script[^>]*>.*?<\/script>/gi,
      /javascript:/gi,
      /on\w+\s*=/gi,
      /<iframe[^>]*>.*?<\/iframe>/gi,
      /<object[^>]*>.*?<\/object>/gi,
      /<embed[^>]*>/gi,
      /<link[^>]*>/gi,
      /<meta[^>]*>/gi,
      /<style[^>]*>.*?<\/style>/gi
    ]

    return xssPatterns.some(pattern => pattern.test(input))
  }
}

// 在 Vue 组件中使用
export default defineComponent({
  setup() {
    const xssProtection = XSSProtection.getInstance()
    const userInput = ref('')
    const sanitizedContent = ref('')
    const isSafe = ref(true)

    const handleInput = (input: string) => {
      userInput.value = input
      
      // 检测 XSS 攻击
      if (xssProtection.detectXSS(input)) {
        isSafe.value = false
        console.warn('Potential XSS attack detected')
        return
      }

      // 清理 HTML 内容
      sanitizedContent.value = xssProtection.sanitizeHtml(input)
      isSafe.value = true
    }

    return {
      userInput,
      sanitizedContent,
      isSafe,
      handleInput
    }
  }
})

Content Security Policy (CSP)

// csp-manager.ts
export class CSPManager {
  private static instance: CSPManager
  private policies: Map<string, string[]> = new Map()

  static getInstance(): CSPManager {
    if (!CSPManager.instance) {
      CSPManager.instance = new CSPManager()
    }
    return CSPManager.instance
  }

  // 设置 CSP 策略
  setPolicy(directive: string, sources: string[]) {
    this.policies.set(directive, sources)
  }

  // 生成 CSP 头部
  generateCSPHeader(): string {
    const directives: string[] = []

    this.policies.forEach((sources, directive) => {
      if (sources.length > 0) {
        directives.push(`${directive} ${sources.join(' ')}`)
      }
    })

    return directives.join('; ')
  }

  // 设置默认安全策略
  setDefaultPolicy() {
    this.setPolicy('default-src', ["'self'"])
    this.setPolicy('script-src', ["'self'", "'unsafe-inline'"])
    this.setPolicy('style-src', ["'self'", "'unsafe-inline'"])
    this.setPolicy('img-src', ["'self'", 'data:', 'https:'])
    this.setPolicy('font-src', ["'self'", 'https:'])
    this.setPolicy('connect-src', ["'self'"])
    this.setPolicy('media-src', ["'self'"])
    this.setPolicy('object-src', ["'none'"])
    this.setPolicy('child-src', ["'self'"])
    this.setPolicy('frame-ancestors', ["'none'"])
    this.setPolicy('form-action', ["'self'"])
    this.setPolicy('base-uri', ["'self'"])
  }

  // 应用 CSP 到页面
  applyCSP() {
    const cspHeader = this.generateCSPHeader()
    
    // 设置 meta 标签
    let metaCSP = document.querySelector('meta[http-equiv="Content-Security-Policy"]')
    if (!metaCSP) {
      metaCSP = document.createElement('meta')
      metaCSP.setAttribute('http-equiv', 'Content-Security-Policy')
      document.head.appendChild(metaCSP)
    }
    metaCSP.setAttribute('content', cspHeader)

    // 监听 CSP 违规事件
    document.addEventListener('securitypolicyviolation', (event) => {
      console.error('CSP Violation:', {
        blockedURI: event.blockedURI,
        violatedDirective: event.violatedDirective,
        originalPolicy: event.originalPolicy
      })
    })
  }

  // 动态添加可信源
  addTrustedSource(directive: string, source: string) {
    const sources = this.policies.get(directive) || []
    if (!sources.includes(source)) {
      sources.push(source)
      this.policies.set(directive, sources)
    }
  }

  // 移除可信源
  removeTrustedSource(directive: string, source: string) {
    const sources = this.policies.get(directive) || []
    const index = sources.indexOf(source)
    if (index > -1) {
      sources.splice(index, 1)
      this.policies.set(directive, sources)
    }
  }
}

// 使用示例
const cspManager = CSPManager.getInstance()

// 设置默认策略
cspManager.setDefaultPolicy()

// 添加可信的 CDN
cspManager.addTrustedSource('script-src', 'https://cdn.jsdelivr.net')
cspManager.addTrustedSource('style-src', 'https://fonts.googleapis.com')

// 应用 CSP
cspManager.applyCSP()

🔒 CSRF 攻击防护

CSRF Token 管理

// csrf-protection.ts
export class CSRFProtection {
  private static instance: CSRFProtection
  private token: string | null = null
  private tokenName = 'X-CSRF-Token'

  static getInstance(): CSRFProtection {
    if (!CSRFProtection.instance) {
      CSRFProtection.instance = new CSRFProtection()
    }
    return CSRFProtection.instance
  }

  // 生成 CSRF Token
  generateToken(): string {
    const array = new Uint8Array(32)
    crypto.getRandomValues(array)
    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('')
  }

  // 获取 CSRF Token
  async getToken(): Promise<string> {
    if (!this.token) {
      this.token = this.generateToken()
      
      // 将 token 存储到 cookie
      document.cookie = `${this.tokenName}=${this.token}; SameSite=Strict; Secure; HttpOnly`
    }
    
    return this.token
  }

  // 验证 CSRF Token
  validateToken(token: string): boolean {
    return this.token === token
  }

  // 在请求中添加 CSRF Token
  addTokenToRequest(headers: HeadersInit = {}): HeadersInit {
    if (this.token) {
      return {
        ...headers,
        [this.tokenName]: this.token
      }
    }
    return headers
  }

  // 刷新 CSRF Token
  refreshToken(): void {
    this.token = this.generateToken()
    document.cookie = `${this.tokenName}=${this.token}; SameSite=Strict; Secure; HttpOnly`
  }

  // 清除 CSRF Token
  clearToken(): void {
    this.token = null
    document.cookie = `${this.tokenName}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
  }
}

// HTTP 请求拦截器
export class SecureHTTPClient {
  private csrfProtection: CSRFProtection
  private baseURL: string

  constructor(baseURL: string) {
    this.csrfProtection = CSRFProtection.getInstance()
    this.baseURL = baseURL
  }

  // 安全的 GET 请求
  async get(url: string, options: RequestInit = {}): Promise<Response> {
    return this.request(url, {
      ...options,
      method: 'GET'
    })
  }

  // 安全的 POST 请求
  async post(url: string, data: any, options: RequestInit = {}): Promise<Response> {
    return this.request(url, {
      ...options,
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
        ...options.headers
      }
    })
  }

  // 安全的 PUT 请求
  async put(url: string, data: any, options: RequestInit = {}): Promise<Response> {
    return this.request(url, {
      ...options,
      method: 'PUT',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
        ...options.headers
      }
    })
  }

  // 安全的 DELETE 请求
  async delete(url: string, options: RequestInit = {}): Promise<Response> {
    return this.request(url, {
      ...options,
      method: 'DELETE'
    })
  }

  // 通用请求方法
  private async request(url: string, options: RequestInit): Promise<Response> {
    const fullUrl = `${this.baseURL}${url}`
    
    // 为修改操作添加 CSRF Token
    if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(options.method || 'GET')) {
      const token = await this.csrfProtection.getToken()
      options.headers = this.csrfProtection.addTokenToRequest(options.headers)
    }

    // 添加安全头
    options.headers = {
      ...options.headers,
      'X-Requested-With': 'XMLHttpRequest',
      'X-Content-Type-Options': 'nosniff',
      'X-Frame-Options': 'DENY'
    }

    const response = await fetch(fullUrl, options)
    
    // 检查响应状态
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
    }

    return response
  }
}

// 使用示例
const httpClient = new SecureHTTPClient('/api')

// 发送安全请求
const response = await httpClient.post('/users', {
  name: 'John Doe',
  email: 'john@example.com'
})

SameSite Cookie 配置

// cookie-security.ts
export class SecureCookieManager {
  private static instance: SecureCookieManager

  static getInstance(): SecureCookieManager {
    if (!SecureCookieManager.instance) {
      SecureCookieManager.instance = new SecureCookieManager()
    }
    return SecureCookieManager.instance
  }

  // 设置安全 Cookie
  setSecureCookie(
    name: string,
    value: string,
    options: {
      expires?: Date
      maxAge?: number
      domain?: string
      path?: string
      secure?: boolean
      sameSite?: 'Strict' | 'Lax' | 'None'
      httpOnly?: boolean
    } = {}
  ): void {
    const {
      expires,
      maxAge,
      domain,
      path = '/',
      secure = true,
      sameSite = 'Strict',
      httpOnly = true
    } = options

    let cookieString = `${name}=${encodeURIComponent(value)}`

    if (expires) {
      cookieString += `; expires=${expires.toUTCString()}`
    }

    if (maxAge) {
      cookieString += `; max-age=${maxAge}`
    }

    if (domain) {
      cookieString += `; domain=${domain}`
    }

    cookieString += `; path=${path}`

    if (secure) {
      cookieString += '; secure'
    }

    cookieString += `; samesite=${sameSite}`

    if (httpOnly) {
      cookieString += '; httponly'
    }

    document.cookie = cookieString
  }

  // 获取 Cookie
  getCookie(name: string): string | null {
    const cookies = document.cookie.split(';')
    
    for (const cookie of cookies) {
      const [cookieName, cookieValue] = cookie.trim().split('=')
      if (cookieName === name) {
        return decodeURIComponent(cookieValue)
      }
    }
    
    return null
  }

  // 删除 Cookie
  deleteCookie(name: string, domain?: string, path: string = '/'): void {
    this.setSecureCookie(name, '', {
      expires: new Date(0),
      domain,
      path
    })
  }

  // 设置会话 Cookie
  setSessionCookie(name: string, value: string): void {
    this.setSecureCookie(name, value, {
      secure: true,
      sameSite: 'Strict',
      httpOnly: true
    })
  }

  // 设置持久化 Cookie
  setPersistentCookie(
    name: string,
    value: string,
    days: number = 30
  ): void {
    const expires = new Date()
    expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
    
    this.setSecureCookie(name, value, {
      expires,
      secure: true,
      sameSite: 'Strict',
      httpOnly: true
    })
  }
}

// 使用示例
const cookieManager = SecureCookieManager.getInstance()

// 设置安全 Cookie
cookieManager.setSecureCookie('sessionId', 'abc123', {
  secure: true,
  sameSite: 'Strict',
  httpOnly: true
})

// 设置持久化 Cookie
cookieManager.setPersistentCookie('userPreferences', JSON.stringify({
  theme: 'dark',
  language: 'zh-CN'
}), 365)

🔐 认证授权与JWT安全

JWT 安全管理

// jwt-manager.ts
export class JWTManager {
  private static instance: JWTManager
  private accessToken: string | null = null
  private refreshToken: string | null = null
  private tokenExpiry: number | null = null

  static getInstance(): JWTManager {
    if (!JWTManager.instance) {
      JWTManager.instance = new JWTManager()
    }
    return JWTManager.instance
  }

  // 解析 JWT Token
  parseToken(token: string): any {
    try {
      const parts = token.split('.')
      if (parts.length !== 3) {
        throw new Error('Invalid JWT format')
      }

      const payload = JSON.parse(atob(parts[1]))
      return payload
    } catch (error) {
      console.error('Failed to parse JWT:', error)
      return null
    }
  }

  // 验证 Token 是否过期
  isTokenExpired(token: string): boolean {
    const payload = this.parseToken(token)
    if (!payload || !payload.exp) {
      return true
    }

    const currentTime = Math.floor(Date.now() / 1000)
    return payload.exp < currentTime
  }

  // 设置访问 Token
  setAccessToken(token: string): void {
    this.accessToken = token
    const payload = this.parseToken(token)
    if (payload && payload.exp) {
      this.tokenExpiry = payload.exp * 1000
    }

    // 存储到安全存储
    this.storeTokenSecurely('accessToken', token)
  }

  // 设置刷新 Token
  setRefreshToken(token: string): void {
    this.refreshToken = token
    this.storeTokenSecurely('refreshToken', token)
  }

  // 获取访问 Token
  getAccessToken(): string | null {
    if (!this.accessToken) {
      this.accessToken = this.getStoredToken('accessToken')
    }
    return this.accessToken
  }

  // 获取刷新 Token
  getRefreshToken(): string | null {
    if (!this.refreshToken) {
      this.refreshToken = this.getStoredToken('refreshToken')
    }
    return this.refreshToken
  }

  // 刷新访问 Token
  async refreshAccessToken(): Promise<boolean> {
    const refreshToken = this.getRefreshToken()
    if (!refreshToken) {
      return false
    }

    try {
      const response = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${refreshToken}`
        }
      })

      if (response.ok) {
        const data = await response.json()
        this.setAccessToken(data.accessToken)
        return true
      }
    } catch (error) {
      console.error('Failed to refresh token:', error)
    }

    return false
  }

  // 清除所有 Token
  clearTokens(): void {
    this.accessToken = null
    this.refreshToken = null
    this.tokenExpiry = null
    
    this.removeStoredToken('accessToken')
    this.removeStoredToken('refreshToken')
  }

  // 安全存储 Token
  private storeTokenSecurely(key: string, token: string): void {
    try {
      // 使用 sessionStorage 存储,页面关闭后自动清除
      sessionStorage.setItem(key, token)
    } catch (error) {
      console.error('Failed to store token:', error)
    }
  }

  // 获取存储的 Token
  private getStoredToken(key: string): string | null {
    try {
      return sessionStorage.getItem(key)
    } catch (error) {
      console.error('Failed to get stored token:', error)
      return null
    }
  }

  // 移除存储的 Token
  private removeStoredToken(key: string): void {
    try {
      sessionStorage.removeItem(key)
    } catch (error) {
      console.error('Failed to remove stored token:', error)
    }
  }

  // 检查 Token 是否需要刷新
  shouldRefreshToken(): boolean {
    if (!this.tokenExpiry) {
      return false
    }

    const currentTime = Date.now()
    const refreshThreshold = 5 * 60 * 1000 // 5分钟前开始刷新

    return this.tokenExpiry - currentTime < refreshThreshold
  }

  // 自动刷新 Token
  startAutoRefresh(): void {
    setInterval(async () => {
      if (this.shouldRefreshToken()) {
        await this.refreshAccessToken()
      }
    }, 60000) // 每分钟检查一次
  }
}

// 认证守卫
export class AuthGuard {
  private static instance: AuthGuard
  private jwtManager: JWTManager

  constructor() {
    this.jwtManager = JWTManager.getInstance()
  }

  static getInstance(): AuthGuard {
    if (!AuthGuard.instance) {
      AuthGuard.instance = new AuthGuard()
    }
    return AuthGuard.instance
  }

  // 检查用户是否已认证
  isAuthenticated(): boolean {
    const token = this.jwtManager.getAccessToken()
    return token !== null && !this.jwtManager.isTokenExpired(token)
  }

  // 获取用户信息
  getUserInfo(): any {
    const token = this.jwtManager.getAccessToken()
    if (!token) {
      return null
    }

    return this.jwtManager.parseToken(token)
  }

  // 检查用户权限
  hasPermission(permission: string): boolean {
    const userInfo = this.getUserInfo()
    if (!userInfo || !userInfo.permissions) {
      return false
    }

    return userInfo.permissions.includes(permission)
  }

  // 检查用户角色
  hasRole(role: string): boolean {
    const userInfo = this.getUserInfo()
    if (!userInfo || !userInfo.roles) {
      return false
    }

    return userInfo.roles.includes(role)
  }

  // 路由守卫
  canActivate(route: any): boolean {
    // 检查是否需要认证
    if (route.meta?.requiresAuth && !this.isAuthenticated()) {
      return false
    }

    // 检查权限
    if (route.meta?.permission && !this.hasPermission(route.meta.permission)) {
      return false
    }

    // 检查角色
    if (route.meta?.role && !this.hasRole(route.meta.role)) {
      return false
    }

    return true
  }
}

// 在 Vue Router 中使用
const authGuard = AuthGuard.getInstance()

router.beforeEach((to, from, next) => {
  if (authGuard.canActivate(to)) {
    next()
  } else {
    next('/login')
  }
})

🔐 数据加密与敏感信息保护

客户端数据加密

// data-encryption.ts
export class DataEncryption {
  private static instance: DataEncryption
  private key: CryptoKey | null = null

  static getInstance(): DataEncryption {
    if (!DataEncryption.instance) {
      DataEncryption.instance = new DataEncryption()
    }
    return DataEncryption.instance
  }

  // 生成加密密钥
  async generateKey(): Promise<CryptoKey> {
    this.key = await crypto.subtle.generateKey(
      {
        name: 'AES-GCM',
        length: 256
      },
      true,
      ['encrypt', 'decrypt']
    )
    return this.key
  }

  // 从密码派生密钥
  async deriveKeyFromPassword(password: string, salt: Uint8Array): Promise<CryptoKey> {
    const keyMaterial = await crypto.subtle.importKey(
      'raw',
      new TextEncoder().encode(password),
      'PBKDF2',
      false,
      ['deriveKey']
    )

    return crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: salt,
        iterations: 100000,
        hash: 'SHA-256'
      },
      keyMaterial,
      {
        name: 'AES-GCM',
        length: 256
      },
      false,
      ['encrypt', 'decrypt']
    )
  }

  // 加密数据
  async encrypt(data: string, key?: CryptoKey): Promise<{
    encrypted: ArrayBuffer
    iv: Uint8Array
  }> {
    const encryptionKey = key || this.key
    if (!encryptionKey) {
      throw new Error('No encryption key available')
    }

    const iv = crypto.getRandomValues(new Uint8Array(12))
    const encodedData = new TextEncoder().encode(data)

    const encrypted = await crypto.subtle.encrypt(
      {
        name: 'AES-GCM',
        iv: iv
      },
      encryptionKey,
      encodedData
    )

    return {
      encrypted,
      iv
    }
  }

  // 解密数据
  async decrypt(
    encrypted: ArrayBuffer,
    iv: Uint8Array,
    key?: CryptoKey
  ): Promise<string> {
    const decryptionKey = key || this.key
    if (!decryptionKey) {
      throw new Error('No decryption key available')
    }

    const decrypted = await crypto.subtle.decrypt(
      {
        name: 'AES-GCM',
        iv: iv
      },
      decryptionKey,
      encrypted
    )

    return new TextDecoder().decode(decrypted)
  }

  // 加密并编码为字符串
  async encryptToString(data: string, key?: CryptoKey): Promise<string> {
    const { encrypted, iv } = await this.encrypt(data, key)
    
    const encryptedArray = new Uint8Array(encrypted)
    const combined = new Uint8Array(iv.length + encryptedArray.length)
    
    combined.set(iv)
    combined.set(encryptedArray, iv.length)
    
    return btoa(String.fromCharCode(...combined))
  }

  // 从字符串解密
  async decryptFromString(encryptedString: string, key?: CryptoKey): Promise<string> {
    const combined = new Uint8Array(
      atob(encryptedString).split('').map(char => char.charCodeAt(0))
    )
    
    const iv = combined.slice(0, 12)
    const encrypted = combined.slice(12)
    
    return this.decrypt(encrypted, iv, key)
  }

  // 哈希数据
  async hash(data: string, algorithm: 'SHA-1' | 'SHA-256' | 'SHA-512' = 'SHA-256'): Promise<string> {
    const encodedData = new TextEncoder().encode(data)
    const hashBuffer = await crypto.subtle.digest(algorithm, encodedData)
    const hashArray = new Uint8Array(hashBuffer)
    
    return Array.from(hashArray)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('')
  }
}

// 敏感数据管理器
export class SensitiveDataManager {
  private static instance: SensitiveDataManager
  private encryption: DataEncryption
  private storageKey = 'encrypted_data'

  static getInstance(): SensitiveDataManager {
    if (!SensitiveDataManager.instance) {
      SensitiveDataManager.instance = new SensitiveDataManager()
    }
    return SensitiveDataManager.instance
  }

  constructor() {
    this.encryption = DataEncryption.getInstance()
  }

  // 存储敏感数据
  async storeSensitiveData(key: string, data: any, password: string): Promise<void> {
    try {
      // 生成盐值
      const salt = crypto.getRandomValues(new Uint8Array(16))
      
      // 派生密钥
      const key = await this.encryption.deriveKeyFromPassword(password, salt)
      
      // 加密数据
      const encryptedData = await this.encryption.encryptToString(JSON.stringify(data), key)
      
      // 存储加密数据和盐值
      const storageData = {
        encrypted: encryptedData,
        salt: Array.from(salt)
      }
      
      localStorage.setItem(`${this.storageKey}_${key}`, JSON.stringify(storageData))
    } catch (error) {
      console.error('Failed to store sensitive data:', error)
      throw error
    }
  }

  // 获取敏感数据
  async getSensitiveData(key: string, password: string): Promise<any> {
    try {
      const storedData = localStorage.getItem(`${this.storageKey}_${key}`)
      if (!storedData) {
        return null
      }

      const { encrypted, salt } = JSON.parse(storedData)
      
      // 派生密钥
      const key = await this.encryption.deriveKeyFromPassword(password, new Uint8Array(salt))
      
      // 解密数据
      const decryptedData = await this.encryption.decryptFromString(encrypted, key)
      
      return JSON.parse(decryptedData)
    } catch (error) {
      console.error('Failed to get sensitive data:', error)
      return null
    }
  }

  // 删除敏感数据
  removeSensitiveData(key: string): void {
    localStorage.removeItem(`${this.storageKey}_${key}`)
  }

  // 清除所有敏感数据
  clearAllSensitiveData(): void {
    const keys = Object.keys(localStorage)
    keys.forEach(key => {
      if (key.startsWith(this.storageKey)) {
        localStorage.removeItem(key)
      }
    })
  }
}

// 使用示例
const sensitiveDataManager = SensitiveDataManager.getInstance()

// 存储敏感数据
await sensitiveDataManager.storeSensitiveData('userCredentials', {
  username: 'john_doe',
  password: 'secret_password'
}, 'user_password')

// 获取敏感数据
const credentials = await sensitiveDataManager.getSensitiveData('userCredentials', 'user_password')
console.log(credentials)

🛡️ 安全头设置

安全响应头管理

// security-headers.ts
export class SecurityHeaders {
  private static instance: SecurityHeaders
  private headers: Map<string, string> = new Map()

  static getInstance(): SecurityHeaders {
    if (!SecurityHeaders.instance) {
      SecurityHeaders.instance = new SecurityHeaders()
    }
    return SecurityHeaders.instance
  }

  // 设置安全头
  setHeader(name: string, value: string): void {
    this.headers.set(name, value)
  }

  // 获取安全头
  getHeader(name: string): string | undefined {
    return this.headers.get(name)
  }

  // 设置默认安全头
  setDefaultHeaders(): void {
    // 防止点击劫持
    this.setHeader('X-Frame-Options', 'DENY')
    
    // 防止 MIME 类型嗅探
    this.setHeader('X-Content-Type-Options', 'nosniff')
    
    // 启用 XSS 过滤器
    this.setHeader('X-XSS-Protection', '1; mode=block')
    
    // 强制 HTTPS
    this.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload')
    
    // 引用者策略
    this.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin')
    
    // 权限策略
    this.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
    
    // 跨域嵌入器策略
    this.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
    this.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
    this.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
  }

  // 应用安全头到响应
  applyHeaders(response: Response): Response {
    const newHeaders = new Headers(response.headers)
    
    this.headers.forEach((value, name) => {
      newHeaders.set(name, value)
    })
    
    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: newHeaders
    })
  }

  // 生成安全头字符串
  generateHeaderString(): string {
    const headerStrings: string[] = []
    
    this.headers.forEach((value, name) => {
      headerStrings.push(`${name}: ${value}`)
    })
    
    return headerStrings.join('\n')
  }
}

// 安全中间件
export class SecurityMiddleware {
  private static instance: SecurityMiddleware
  private securityHeaders: SecurityHeaders

  static getInstance(): SecurityMiddleware {
    if (!SecurityMiddleware.instance) {
      SecurityMiddleware.instance = new SecurityMiddleware()
    }
    return SecurityMiddleware.instance
  }

  constructor() {
    this.securityHeaders = SecurityHeaders.getInstance()
    this.securityHeaders.setDefaultHeaders()
  }

  // 处理请求
  async handleRequest(request: Request): Promise<Response> {
    // 检查请求方法
    if (!this.isAllowedMethod(request.method)) {
      return new Response('Method Not Allowed', { status: 405 })
    }

    // 检查请求头
    if (!this.isValidRequest(request)) {
      return new Response('Bad Request', { status: 400 })
    }

    // 处理请求
    const response = await this.processRequest(request)
    
    // 应用安全头
    return this.securityHeaders.applyHeaders(response)
  }

  // 检查允许的请求方法
  private isAllowedMethod(method: string): boolean {
    const allowedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']
    return allowedMethods.includes(method)
  }

  // 验证请求
  private isValidRequest(request: Request): boolean {
    // 检查 Content-Type
    const contentType = request.headers.get('Content-Type')
    if (request.method !== 'GET' && request.method !== 'HEAD') {
      if (!contentType || !contentType.includes('application/json')) {
        return false
      }
    }

    // 检查 User-Agent
    const userAgent = request.headers.get('User-Agent')
    if (!userAgent || userAgent.length > 500) {
      return false
    }

    return true
  }

  // 处理请求
  private async processRequest(request: Request): Promise<Response> {
    // 这里可以添加具体的请求处理逻辑
    return new Response('OK', { status: 200 })
  }
}

// 使用示例
const securityMiddleware = SecurityMiddleware.getInstance()

// 处理请求
const response = await securityMiddleware.handleRequest(request)

🎯 专题总结

通过本专题学习,你掌握了:

  1. XSS 攻击防护:HTML转义、内容清理、CSP策略
  2. CSRF 攻击防护:Token验证、SameSite Cookie、安全请求
  3. 认证授权安全:JWT管理、权限控制、路由守卫
  4. 数据加密保护:客户端加密、敏感数据管理、哈希算法
  5. 安全头设置:响应头配置、安全中间件、防护策略

📝 练习题

  1. 实现一个完整的XSS防护系统
  2. 开发一个CSRF防护中间件
  3. 构建一个安全的认证授权系统
  4. 创建一个数据加密存储方案
  5. 实现安全头配置管理

🔗 相关资源

  • OWASP 前端安全指南
  • CSP 规范
  • JWT 安全最佳实践
  • Web 加密 API
  • 前端安全防护
Prev
CSS 变量体系与主题系统深度指南
Next
CSS 专题:动效优化与微交互