Skip to content

安全指南

本指南介绍如何在 Trae 应用程序中实施安全最佳实践,包括身份验证、授权、数据保护、安全配置等方面。

概述

安全性是现代 Web 应用程序的基础要求。Trae 提供了多层安全防护机制,帮助您构建安全可靠的应用程序。本指南将涵盖从前端到后端的全方位安全实践。

身份验证

JWT 身份验证

javascript
// JWT 工具类
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';

class AuthService {
  constructor() {
    this.secretKey = process.env.JWT_SECRET;
    this.refreshSecretKey = process.env.JWT_REFRESH_SECRET;
    this.tokenExpiry = '15m';
    this.refreshTokenExpiry = '7d';
  }
  
  // 生成访问令牌
  generateAccessToken(payload) {
    return jwt.sign(payload, this.secretKey, {
      expiresIn: this.tokenExpiry,
      issuer: 'trae-app',
      audience: 'trae-users'
    });
  }
  
  // 生成刷新令牌
  generateRefreshToken(payload) {
    return jwt.sign(payload, this.refreshSecretKey, {
      expiresIn: this.refreshTokenExpiry,
      issuer: 'trae-app',
      audience: 'trae-users'
    });
  }
  
  // 验证访问令牌
  verifyAccessToken(token) {
    try {
      return jwt.verify(token, this.secretKey, {
        issuer: 'trae-app',
        audience: 'trae-users'
      });
    } catch (error) {
      throw new Error('Invalid access token');
    }
  }
  
  // 验证刷新令牌
  verifyRefreshToken(token) {
    try {
      return jwt.verify(token, this.refreshSecretKey, {
        issuer: 'trae-app',
        audience: 'trae-users'
      });
    } catch (error) {
      throw new Error('Invalid refresh token');
    }
  }
  
  // 密码哈希
  async hashPassword(password) {
    const saltRounds = 12;
    return bcrypt.hash(password, saltRounds);
  }
  
  // 密码验证
  async verifyPassword(password, hashedPassword) {
    return bcrypt.compare(password, hashedPassword);
  }
  
  // 生成安全的随机令牌
  generateSecureToken(length = 32) {
    const crypto = require('crypto');
    return crypto.randomBytes(length).toString('hex');
  }
}

const authService = new AuthService();
export default authService;

前端身份验证

javascript
// 身份验证上下文
import { createContext, useContext, useReducer, useEffect } from 'react';
import authService from '../services/authService';

const AuthContext = createContext();

const authReducer = (state, action) => {
  switch (action.type) {
    case 'LOGIN_START':
      return { ...state, loading: true, error: null };
    
    case 'LOGIN_SUCCESS':
      return {
        ...state,
        loading: false,
        isAuthenticated: true,
        user: action.payload.user,
        token: action.payload.token
      };
    
    case 'LOGIN_FAILURE':
      return {
        ...state,
        loading: false,
        error: action.payload,
        isAuthenticated: false,
        user: null,
        token: null
      };
    
    case 'LOGOUT':
      return {
        ...state,
        isAuthenticated: false,
        user: null,
        token: null,
        error: null
      };
    
    case 'TOKEN_REFRESH':
      return {
        ...state,
        token: action.payload
      };
    
    default:
      return state;
  }
};

const initialState = {
  isAuthenticated: false,
  user: null,
  token: null,
  loading: false,
  error: null
};

export function AuthProvider({ children }) {
  const [state, dispatch] = useReducer(authReducer, initialState);
  
  // 自动令牌刷新
  useEffect(() => {
    const refreshToken = localStorage.getItem('refreshToken');
    if (refreshToken) {
      refreshAccessToken(refreshToken);
    }
    
    // 设置定时刷新
    const interval = setInterval(() => {
      const currentRefreshToken = localStorage.getItem('refreshToken');
      if (currentRefreshToken && state.isAuthenticated) {
        refreshAccessToken(currentRefreshToken);
      }
    }, 14 * 60 * 1000); // 14分钟刷新一次
    
    return () => clearInterval(interval);
  }, [state.isAuthenticated]);
  
  const login = async (email, password) => {
    dispatch({ type: 'LOGIN_START' });
    
    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ email, password })
      });
      
      if (!response.ok) {
        throw new Error('登录失败');
      }
      
      const data = await response.json();
      
      // 安全存储令牌
      localStorage.setItem('refreshToken', data.refreshToken);
      
      dispatch({
        type: 'LOGIN_SUCCESS',
        payload: {
          user: data.user,
          token: data.accessToken
        }
      });
      
      return data;
    } catch (error) {
      dispatch({
        type: 'LOGIN_FAILURE',
        payload: error.message
      });
      throw error;
    }
  };
  
  const logout = async () => {
    try {
      const refreshToken = localStorage.getItem('refreshToken');
      if (refreshToken) {
        await fetch('/api/auth/logout', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${state.token}`
          },
          body: JSON.stringify({ refreshToken })
        });
      }
    } catch (error) {
      console.error('登出请求失败:', error);
    } finally {
      localStorage.removeItem('refreshToken');
      dispatch({ type: 'LOGOUT' });
    }
  };
  
  const refreshAccessToken = async (refreshToken) => {
    try {
      const response = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ refreshToken })
      });
      
      if (!response.ok) {
        throw new Error('令牌刷新失败');
      }
      
      const data = await response.json();
      
      dispatch({
        type: 'TOKEN_REFRESH',
        payload: data.accessToken
      });
      
      return data.accessToken;
    } catch (error) {
      console.error('令牌刷新失败:', error);
      logout();
    }
  };
  
  const value = {
    ...state,
    login,
    logout,
    refreshAccessToken
  };
  
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

受保护的路由

javascript
// 路由保护组件
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

function ProtectedRoute({ children, requiredRoles = [] }) {
  const { isAuthenticated, user, loading } = useAuth();
  const location = useLocation();
  
  if (loading) {
    return (
      <div className="flex items-center justify-center h-screen">
        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-blue-500"></div>
      </div>
    );
  }
  
  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  
  // 角色检查
  if (requiredRoles.length > 0) {
    const hasRequiredRole = requiredRoles.some(role => 
      user.roles?.includes(role)
    );
    
    if (!hasRequiredRole) {
      return <Navigate to="/unauthorized" replace />;
    }
  }
  
  return children;
}

// 使用示例
function App() {
  return (
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/" element={<HomePage />} />
      
      {/* 需要登录的路由 */}
      <Route path="/dashboard" element={
        <ProtectedRoute>
          <Dashboard />
        </ProtectedRoute>
      } />
      
      {/* 需要管理员权限的路由 */}
      <Route path="/admin" element={
        <ProtectedRoute requiredRoles={['admin']}>
          <AdminPanel />
        </ProtectedRoute>
      } />
      
      {/* 需要特定权限的路由 */}
      <Route path="/settings" element={
        <ProtectedRoute requiredRoles={['admin', 'moderator']}>
          <Settings />
        </ProtectedRoute>
      } />
    </Routes>
  );
}

授权和权限控制

基于角色的访问控制 (RBAC)

javascript
// 权限管理系统
class PermissionManager {
  constructor() {
    this.roles = new Map();
    this.permissions = new Map();
    this.userRoles = new Map();
    
    this.initializeDefaultRoles();
  }
  
  initializeDefaultRoles() {
    // 定义权限
    this.permissions.set('read:projects', { name: '查看项目', description: '可以查看项目列表和详情' });
    this.permissions.set('create:projects', { name: '创建项目', description: '可以创建新项目' });
    this.permissions.set('update:projects', { name: '更新项目', description: '可以修改项目信息' });
    this.permissions.set('delete:projects', { name: '删除项目', description: '可以删除项目' });
    this.permissions.set('manage:users', { name: '用户管理', description: '可以管理用户账户' });
    this.permissions.set('admin:system', { name: '系统管理', description: '可以管理系统设置' });
    
    // 定义角色
    this.roles.set('viewer', {
      name: '查看者',
      permissions: ['read:projects']
    });
    
    this.roles.set('editor', {
      name: '编辑者',
      permissions: ['read:projects', 'create:projects', 'update:projects']
    });
    
    this.roles.set('admin', {
      name: '管理员',
      permissions: [
        'read:projects',
        'create:projects',
        'update:projects',
        'delete:projects',
        'manage:users'
      ]
    });
    
    this.roles.set('superadmin', {
      name: '超级管理员',
      permissions: [
        'read:projects',
        'create:projects',
        'update:projects',
        'delete:projects',
        'manage:users',
        'admin:system'
      ]
    });
  }
  
  // 分配角色给用户
  assignRole(userId, roleName) {
    if (!this.roles.has(roleName)) {
      throw new Error(`角色 ${roleName} 不存在`);
    }
    
    if (!this.userRoles.has(userId)) {
      this.userRoles.set(userId, new Set());
    }
    
    this.userRoles.get(userId).add(roleName);
  }
  
  // 移除用户角色
  removeRole(userId, roleName) {
    if (this.userRoles.has(userId)) {
      this.userRoles.get(userId).delete(roleName);
    }
  }
  
  // 检查用户是否有特定权限
  hasPermission(userId, permission) {
    const userRoles = this.userRoles.get(userId);
    if (!userRoles) return false;
    
    for (const roleName of userRoles) {
      const role = this.roles.get(roleName);
      if (role && role.permissions.includes(permission)) {
        return true;
      }
    }
    
    return false;
  }
  
  // 检查用户是否有特定角色
  hasRole(userId, roleName) {
    const userRoles = this.userRoles.get(userId);
    return userRoles ? userRoles.has(roleName) : false;
  }
  
  // 获取用户所有权限
  getUserPermissions(userId) {
    const userRoles = this.userRoles.get(userId);
    if (!userRoles) return [];
    
    const permissions = new Set();
    
    for (const roleName of userRoles) {
      const role = this.roles.get(roleName);
      if (role) {
        role.permissions.forEach(permission => permissions.add(permission));
      }
    }
    
    return Array.from(permissions);
  }
  
  // 创建自定义角色
  createRole(roleName, permissions) {
    // 验证权限是否存在
    for (const permission of permissions) {
      if (!this.permissions.has(permission)) {
        throw new Error(`权限 ${permission} 不存在`);
      }
    }
    
    this.roles.set(roleName, {
      name: roleName,
      permissions: [...permissions],
      custom: true
    });
  }
}

const permissionManager = new PermissionManager();
export default permissionManager;

权限检查 Hook

javascript
// 权限检查 Hook
import { useAuth } from '../contexts/AuthContext';
import permissionManager from '../services/permissionManager';

export function usePermissions() {
  const { user, isAuthenticated } = useAuth();
  
  const hasPermission = (permission) => {
    if (!isAuthenticated || !user) return false;
    return permissionManager.hasPermission(user.id, permission);
  };
  
  const hasRole = (role) => {
    if (!isAuthenticated || !user) return false;
    return permissionManager.hasRole(user.id, role);
  };
  
  const hasAnyRole = (roles) => {
    if (!isAuthenticated || !user) return false;
    return roles.some(role => permissionManager.hasRole(user.id, role));
  };
  
  const hasAllPermissions = (permissions) => {
    if (!isAuthenticated || !user) return false;
    return permissions.every(permission => 
      permissionManager.hasPermission(user.id, permission)
    );
  };
  
  const getUserPermissions = () => {
    if (!isAuthenticated || !user) return [];
    return permissionManager.getUserPermissions(user.id);
  };
  
  return {
    hasPermission,
    hasRole,
    hasAnyRole,
    hasAllPermissions,
    getUserPermissions
  };
}

// 权限组件
export function PermissionGate({ permission, role, children, fallback = null }) {
  const { hasPermission, hasRole } = usePermissions();
  
  const hasAccess = permission ? hasPermission(permission) : hasRole(role);
  
  return hasAccess ? children : fallback;
}

// 使用示例
function ProjectActions({ project }) {
  return (
    <div className="project-actions">
      <PermissionGate permission="read:projects">
        <button>查看详情</button>
      </PermissionGate>
      
      <PermissionGate permission="update:projects">
        <button>编辑项目</button>
      </PermissionGate>
      
      <PermissionGate permission="delete:projects">
        <button className="text-red-500">删除项目</button>
      </PermissionGate>
      
      <PermissionGate role="admin">
        <button>管理设置</button>
      </PermissionGate>
    </div>
  );
}

数据保护

输入验证和清理

javascript
// 输入验证工具
import validator from 'validator';
import DOMPurify from 'dompurify';

class InputValidator {
  // 验证邮箱
  static validateEmail(email) {
    if (!email || typeof email !== 'string') {
      throw new Error('邮箱地址不能为空');
    }
    
    if (!validator.isEmail(email)) {
      throw new Error('邮箱地址格式无效');
    }
    
    return validator.normalizeEmail(email);
  }
  
  // 验证密码强度
  static validatePassword(password) {
    if (!password || typeof password !== 'string') {
      throw new Error('密码不能为空');
    }
    
    const minLength = 8;
    const maxLength = 128;
    
    if (password.length < minLength) {
      throw new Error(`密码长度不能少于 ${minLength} 个字符`);
    }
    
    if (password.length > maxLength) {
      throw new Error(`密码长度不能超过 ${maxLength} 个字符`);
    }
    
    // 检查密码复杂度
    const hasUpperCase = /[A-Z]/.test(password);
    const hasLowerCase = /[a-z]/.test(password);
    const hasNumbers = /\d/.test(password);
    const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
    
    const complexityScore = [hasUpperCase, hasLowerCase, hasNumbers, hasSpecialChar]
      .filter(Boolean).length;
    
    if (complexityScore < 3) {
      throw new Error('密码必须包含大写字母、小写字母、数字和特殊字符中的至少三种');
    }
    
    return password;
  }
  
  // 验证用户名
  static validateUsername(username) {
    if (!username || typeof username !== 'string') {
      throw new Error('用户名不能为空');
    }
    
    const cleaned = username.trim();
    
    if (cleaned.length < 3 || cleaned.length > 30) {
      throw new Error('用户名长度必须在 3-30 个字符之间');
    }
    
    if (!/^[a-zA-Z0-9_-]+$/.test(cleaned)) {
      throw new Error('用户名只能包含字母、数字、下划线和连字符');
    }
    
    return cleaned;
  }
  
  // 清理 HTML 内容
  static sanitizeHtml(html) {
    if (!html || typeof html !== 'string') {
      return '';
    }
    
    return DOMPurify.sanitize(html, {
      ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
      ALLOWED_ATTR: ['href', 'target', 'rel'],
      ALLOW_DATA_ATTR: false
    });
  }
  
  // 验证 URL
  static validateUrl(url) {
    if (!url || typeof url !== 'string') {
      throw new Error('URL 不能为空');
    }
    
    if (!validator.isURL(url, {
      protocols: ['http', 'https'],
      require_protocol: true
    })) {
      throw new Error('URL 格式无效');
    }
    
    return url;
  }
  
  // 验证文件上传
  static validateFile(file, options = {}) {
    const {
      maxSize = 5 * 1024 * 1024, // 5MB
      allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
      allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp']
    } = options;
    
    if (!file) {
      throw new Error('文件不能为空');
    }
    
    if (file.size > maxSize) {
      throw new Error(`文件大小不能超过 ${Math.round(maxSize / 1024 / 1024)}MB`);
    }
    
    if (!allowedTypes.includes(file.type)) {
      throw new Error(`不支持的文件类型: ${file.type}`);
    }
    
    const extension = file.name.toLowerCase().substring(file.name.lastIndexOf('.'));
    if (!allowedExtensions.includes(extension)) {
      throw new Error(`不支持的文件扩展名: ${extension}`);
    }
    
    return true;
  }
  
  // 防止 SQL 注入(用于动态查询构建)
  static escapeSql(value) {
    if (typeof value === 'string') {
      return value.replace(/[\0\x08\x09\x1a\n\r"'\\%]/g, (char) => {
        switch (char) {
          case '\0': return '\\0';
          case '\x08': return '\\b';
          case '\x09': return '\\t';
          case '\x1a': return '\\z';
          case '\n': return '\\n';
          case '\r': return '\\r';
          case '"':
          case "'":
          case '\\':
          case '%': return '\\' + char;
          default: return char;
        }
      });
    }
    return value;
  }
}

export default InputValidator;

数据加密

javascript
// 数据加密工具
import crypto from 'crypto';

class EncryptionService {
  constructor() {
    this.algorithm = 'aes-256-gcm';
    this.keyLength = 32;
    this.ivLength = 16;
    this.tagLength = 16;
    this.secretKey = process.env.ENCRYPTION_KEY;
    
    if (!this.secretKey) {
      throw new Error('ENCRYPTION_KEY environment variable is required');
    }
  }
  
  // 生成密钥
  generateKey() {
    return crypto.randomBytes(this.keyLength);
  }
  
  // 加密数据
  encrypt(text) {
    try {
      const iv = crypto.randomBytes(this.ivLength);
      const cipher = crypto.createCipher(this.algorithm, this.secretKey);
      cipher.setAAD(Buffer.from('trae-app', 'utf8'));
      
      let encrypted = cipher.update(text, 'utf8', 'hex');
      encrypted += cipher.final('hex');
      
      const tag = cipher.getAuthTag();
      
      return {
        encrypted,
        iv: iv.toString('hex'),
        tag: tag.toString('hex')
      };
    } catch (error) {
      throw new Error('加密失败: ' + error.message);
    }
  }
  
  // 解密数据
  decrypt(encryptedData) {
    try {
      const { encrypted, iv, tag } = encryptedData;
      
      const decipher = crypto.createDecipher(this.algorithm, this.secretKey);
      decipher.setAAD(Buffer.from('trae-app', 'utf8'));
      decipher.setAuthTag(Buffer.from(tag, 'hex'));
      
      let decrypted = decipher.update(encrypted, 'hex', 'utf8');
      decrypted += decipher.final('utf8');
      
      return decrypted;
    } catch (error) {
      throw new Error('解密失败: ' + error.message);
    }
  }
  
  // 哈希数据
  hash(data, salt = null) {
    const actualSalt = salt || crypto.randomBytes(16).toString('hex');
    const hash = crypto.pbkdf2Sync(data, actualSalt, 10000, 64, 'sha512');
    
    return {
      hash: hash.toString('hex'),
      salt: actualSalt
    };
  }
  
  // 验证哈希
  verifyHash(data, hash, salt) {
    const newHash = crypto.pbkdf2Sync(data, salt, 10000, 64, 'sha512');
    return newHash.toString('hex') === hash;
  }
  
  // 生成安全的随机字符串
  generateSecureRandom(length = 32) {
    return crypto.randomBytes(length).toString('hex');
  }
  
  // 生成 HMAC 签名
  generateHMAC(data, secret = null) {
    const key = secret || this.secretKey;
    return crypto.createHmac('sha256', key).update(data).digest('hex');
  }
  
  // 验证 HMAC 签名
  verifyHMAC(data, signature, secret = null) {
    const expectedSignature = this.generateHMAC(data, secret);
    return crypto.timingSafeEqual(
      Buffer.from(signature, 'hex'),
      Buffer.from(expectedSignature, 'hex')
    );
  }
}

const encryptionService = new EncryptionService();
export default encryptionService;

安全配置

安全头设置

javascript
// 安全中间件
function securityMiddleware(req, res, next) {
  // 设置安全头
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
  
  // 内容安全策略
  const csp = [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: https:",
    "connect-src 'self' https://api.trae.com",
    "frame-ancestors 'none'",
    "base-uri 'self'",
    "form-action 'self'"
  ].join('; ');
  
  res.setHeader('Content-Security-Policy', csp);
  
  // HTTPS 强制
  if (process.env.NODE_ENV === 'production') {
    res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
  }
  
  next();
}

// 速率限制
import rateLimit from 'express-rate-limit';

const createRateLimiter = (windowMs, max, message) => {
  return rateLimit({
    windowMs,
    max,
    message: { error: message },
    standardHeaders: true,
    legacyHeaders: false,
    handler: (req, res) => {
      res.status(429).json({
        error: message,
        retryAfter: Math.round(windowMs / 1000)
      });
    }
  });
};

// 不同端点的速率限制
const authLimiter = createRateLimiter(
  15 * 60 * 1000, // 15分钟
  5, // 最多5次尝试
  '登录尝试次数过多,请稍后再试'
);

const apiLimiter = createRateLimiter(
  15 * 60 * 1000, // 15分钟
  100, // 最多100次请求
  'API 请求频率过高,请稍后再试'
);

const uploadLimiter = createRateLimiter(
  60 * 60 * 1000, // 1小时
  10, // 最多10次上传
  '文件上传频率过高,请稍后再试'
);

export { securityMiddleware, authLimiter, apiLimiter, uploadLimiter };

环境变量管理

javascript
// 环境变量验证
import Joi from 'joi';

const envSchema = Joi.object({
  NODE_ENV: Joi.string().valid('development', 'production', 'test').required(),
  PORT: Joi.number().port().default(3000),
  
  // 数据库配置
  DATABASE_URL: Joi.string().uri().required(),
  DATABASE_SSL: Joi.boolean().default(false),
  
  // JWT 配置
  JWT_SECRET: Joi.string().min(32).required(),
  JWT_REFRESH_SECRET: Joi.string().min(32).required(),
  JWT_EXPIRES_IN: Joi.string().default('15m'),
  JWT_REFRESH_EXPIRES_IN: Joi.string().default('7d'),
  
  // 加密配置
  ENCRYPTION_KEY: Joi.string().length(64).required(),
  
  // 外部服务配置
  REDIS_URL: Joi.string().uri(),
  EMAIL_SERVICE_API_KEY: Joi.string(),
  STORAGE_SERVICE_KEY: Joi.string(),
  
  // 安全配置
  CORS_ORIGIN: Joi.string().default('*'),
  RATE_LIMIT_WINDOW: Joi.number().default(900000), // 15分钟
  RATE_LIMIT_MAX: Joi.number().default(100),
  
  // 日志配置
  LOG_LEVEL: Joi.string().valid('error', 'warn', 'info', 'debug').default('info'),
  LOG_FILE: Joi.string(),
  
  // 监控配置
  SENTRY_DSN: Joi.string().uri(),
  ANALYTICS_ID: Joi.string()
}).unknown();

const { error, value: envVars } = envSchema.validate(process.env);

if (error) {
  throw new Error(`环境变量配置错误: ${error.message}`);
}

// 导出验证后的环境变量
export const config = {
  env: envVars.NODE_ENV,
  port: envVars.PORT,
  
  database: {
    url: envVars.DATABASE_URL,
    ssl: envVars.DATABASE_SSL
  },
  
  jwt: {
    secret: envVars.JWT_SECRET,
    refreshSecret: envVars.JWT_REFRESH_SECRET,
    expiresIn: envVars.JWT_EXPIRES_IN,
    refreshExpiresIn: envVars.JWT_REFRESH_EXPIRES_IN
  },
  
  encryption: {
    key: envVars.ENCRYPTION_KEY
  },
  
  redis: {
    url: envVars.REDIS_URL
  },
  
  security: {
    corsOrigin: envVars.CORS_ORIGIN,
    rateLimitWindow: envVars.RATE_LIMIT_WINDOW,
    rateLimitMax: envVars.RATE_LIMIT_MAX
  },
  
  logging: {
    level: envVars.LOG_LEVEL,
    file: envVars.LOG_FILE
  },
  
  monitoring: {
    sentryDsn: envVars.SENTRY_DSN,
    analyticsId: envVars.ANALYTICS_ID
  }
};

安全审计

安全日志记录

javascript
// 安全日志记录器
import winston from 'winston';
import { config } from './config';

class SecurityLogger {
  constructor() {
    this.logger = winston.createLogger({
      level: config.logging.level,
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.errors({ stack: true }),
        winston.format.json()
      ),
      defaultMeta: { service: 'trae-security' },
      transports: [
        new winston.transports.File({ 
          filename: 'logs/security-error.log', 
          level: 'error' 
        }),
        new winston.transports.File({ 
          filename: 'logs/security-combined.log' 
        })
      ]
    });
    
    if (config.env !== 'production') {
      this.logger.add(new winston.transports.Console({
        format: winston.format.simple()
      }));
    }
  }
  
  // 记录登录尝试
  logLoginAttempt(email, success, ip, userAgent) {
    this.logger.info('Login attempt', {
      event: 'login_attempt',
      email,
      success,
      ip,
      userAgent,
      timestamp: new Date().toISOString()
    });
  }
  
  // 记录失败的登录尝试
  logFailedLogin(email, reason, ip, userAgent) {
    this.logger.warn('Failed login attempt', {
      event: 'failed_login',
      email,
      reason,
      ip,
      userAgent,
      timestamp: new Date().toISOString()
    });
  }
  
  // 记录权限违规
  logPermissionViolation(userId, action, resource, ip) {
    this.logger.warn('Permission violation', {
      event: 'permission_violation',
      userId,
      action,
      resource,
      ip,
      timestamp: new Date().toISOString()
    });
  }
  
  // 记录可疑活动
  logSuspiciousActivity(type, details, ip, userAgent) {
    this.logger.error('Suspicious activity detected', {
      event: 'suspicious_activity',
      type,
      details,
      ip,
      userAgent,
      timestamp: new Date().toISOString()
    });
  }
  
  // 记录数据访问
  logDataAccess(userId, action, resource, sensitive = false) {
    this.logger.info('Data access', {
      event: 'data_access',
      userId,
      action,
      resource,
      sensitive,
      timestamp: new Date().toISOString()
    });
  }
  
  // 记录配置更改
  logConfigChange(userId, setting, oldValue, newValue) {
    this.logger.info('Configuration change', {
      event: 'config_change',
      userId,
      setting,
      oldValue: oldValue ? '[REDACTED]' : null,
      newValue: newValue ? '[REDACTED]' : null,
      timestamp: new Date().toISOString()
    });
  }
}

const securityLogger = new SecurityLogger();
export default securityLogger;

漏洞扫描

javascript
// 安全扫描工具
class SecurityScanner {
  constructor() {
    this.vulnerabilities = [];
  }
  
  // 扫描依赖漏洞
  async scanDependencies() {
    try {
      const { execSync } = require('child_process');
      const auditResult = execSync('npm audit --json', { encoding: 'utf8' });
      const audit = JSON.parse(auditResult);
      
      if (audit.vulnerabilities) {
        Object.entries(audit.vulnerabilities).forEach(([name, vuln]) => {
          if (vuln.severity === 'high' || vuln.severity === 'critical') {
            this.vulnerabilities.push({
              type: 'dependency',
              package: name,
              severity: vuln.severity,
              title: vuln.title,
              url: vuln.url
            });
          }
        });
      }
    } catch (error) {
      console.error('依赖扫描失败:', error.message);
    }
  }
  
  // 扫描配置安全性
  scanConfiguration() {
    const issues = [];
    
    // 检查环境变量
    if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
      issues.push({
        type: 'config',
        severity: 'high',
        message: 'JWT_SECRET 太短或未设置'
      });
    }
    
    if (!process.env.ENCRYPTION_KEY) {
      issues.push({
        type: 'config',
        severity: 'critical',
        message: 'ENCRYPTION_KEY 未设置'
      });
    }
    
    // 检查生产环境配置
    if (process.env.NODE_ENV === 'production') {
      if (!process.env.HTTPS) {
        issues.push({
          type: 'config',
          severity: 'high',
          message: '生产环境未启用 HTTPS'
        });
      }
      
      if (process.env.CORS_ORIGIN === '*') {
        issues.push({
          type: 'config',
          severity: 'medium',
          message: 'CORS 配置过于宽松'
        });
      }
    }
    
    this.vulnerabilities.push(...issues);
  }
  
  // 扫描代码安全性
  scanCode() {
    const issues = [];
    
    // 这里可以集成静态代码分析工具
    // 例如 ESLint 安全规则、SonarQube 等
    
    return issues;
  }
  
  // 生成安全报告
  generateReport() {
    const report = {
      timestamp: new Date().toISOString(),
      totalVulnerabilities: this.vulnerabilities.length,
      critical: this.vulnerabilities.filter(v => v.severity === 'critical').length,
      high: this.vulnerabilities.filter(v => v.severity === 'high').length,
      medium: this.vulnerabilities.filter(v => v.severity === 'medium').length,
      low: this.vulnerabilities.filter(v => v.severity === 'low').length,
      vulnerabilities: this.vulnerabilities
    };
    
    return report;
  }
  
  // 运行完整扫描
  async runFullScan() {
    console.log('开始安全扫描...');
    
    await this.scanDependencies();
    this.scanConfiguration();
    this.scanCode();
    
    const report = this.generateReport();
    
    console.log(`扫描完成。发现 ${report.totalVulnerabilities} 个安全问题`);
    console.log(`严重: ${report.critical}, 高危: ${report.high}, 中危: ${report.medium}, 低危: ${report.low}`);
    
    return report;
  }
}

// 定期安全扫描
if (process.env.NODE_ENV === 'production') {
  const scanner = new SecurityScanner();
  
  // 每天运行一次安全扫描
  setInterval(async () => {
    try {
      const report = await scanner.runFullScan();
      
      // 如果发现严重或高危漏洞,发送警报
      if (report.critical > 0 || report.high > 0) {
        // 发送警报邮件或通知
        console.error('发现严重安全漏洞,请立即处理!');
      }
    } catch (error) {
      console.error('安全扫描失败:', error);
    }
  }, 24 * 60 * 60 * 1000); // 24小时
}

export default SecurityScanner;

最佳实践

安全检查清单

  • [ ] 身份验证

    • [ ] 实施强密码策略
    • [ ] 使用多因素认证
    • [ ] 实施账户锁定机制
    • [ ] 安全存储密码(哈希 + 盐)
  • [ ] 授权

    • [ ] 实施最小权限原则
    • [ ] 使用基于角色的访问控制
    • [ ] 定期审查权限
    • [ ] 实施权限分离
  • [ ] 数据保护

    • [ ] 加密敏感数据
    • [ ] 验证和清理所有输入
    • [ ] 实施数据备份策略
    • [ ] 遵循数据保护法规
  • [ ] 网络安全

    • [ ] 使用 HTTPS
    • [ ] 配置安全头
    • [ ] 实施 CORS 策略
    • [ ] 使用内容安全策略
  • [ ] 监控和审计

    • [ ] 记录安全事件
    • [ ] 监控异常活动
    • [ ] 定期安全扫描
    • [ ] 实施入侵检测

安全开发生命周期

javascript
// 安全开发流程检查
const securityChecklist = {
  planning: [
    '识别安全需求',
    '进行威胁建模',
    '定义安全架构',
    '制定安全策略'
  ],
  
  development: [
    '遵循安全编码标准',
    '使用安全的库和框架',
    '实施输入验证',
    '进行代码审查'
  ],
  
  testing: [
    '进行安全测试',
    '漏洞扫描',
    '渗透测试',
    '依赖安全检查'
  ],
  
  deployment: [
    '安全配置检查',
    '环境加固',
    '监控设置',
    '应急响应计划'
  ],
  
  maintenance: [
    '定期安全更新',
    '持续监控',
    '安全审计',
    '事件响应'
  ]
};

// 安全培训材料
const securityTraining = {
  topics: [
    'OWASP Top 10',
    '安全编码实践',
    '密码学基础',
    '网络安全',
    '数据保护',
    '事件响应'
  ],
  
  resources: [
    'https://owasp.org/www-project-top-ten/',
    'https://cheatsheetseries.owasp.org/',
    'https://www.sans.org/white-papers/',
    'https://nvd.nist.gov/'
  ]
};

export { securityChecklist, securityTraining };

通过遵循这些安全指南和最佳实践,您可以构建出安全可靠的 Trae 应用程序,保护用户数据和系统安全。记住,安全是一个持续的过程,需要定期更新和改进。

您的终极 AI 驱动 IDE 学习指南