安全指南
本指南介绍如何在 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 应用程序,保护用户数据和系统安全。记住,安全是一个持续的过程,需要定期更新和改进。