正在加载文档...
文档内容较大,正在处理中,请稍候
正在加载文档...
文档内容较大,正在处理中,请稍候
本文档详细说明了前后端系统实现的安全防护措施,涵盖认证、授权、数据保护、攻击防护等多个方面,确保系统的安全性和可靠性。
本文档按照 "已实现" 和 "可优化" 两个维度组织内容:
每个章节都会明确标注各项安全措施的实现状态,方便开发团队了解当前安全状况和后续优化方向。
系统采用多层安全防护体系:
| 攻击类型 | 防护措施 | 状态 |
|---|---|---|
| SQL 注入 | 参数化查询 | ✅ 已防护 |
| XSS 攻击 | 数据脱敏、输入验证 | ✅ 已防护 |
| CSRF 攻击 | Token 验证、CORS 配置 | ✅ 已防护 |
| 暴力破解 | 验证码、密码强度验证 | ✅ 已防护 |
| 会话劫持 | JWT Token、单设备登录 | ✅ 已防护 |
| 数据泄露 | 敏感信息脱敏 | ✅ 已防护 |
| 未授权访问 | 权限中间件 | ✅ 已防护 |
| 章节 | 安全措施 | 状态 | 说明 |
|---|---|---|---|
| 2. 认证安全 | |||
| 2.1 | JWT Token 认证 | ✅ 已实现 | 完整的 Token 生成、验证、刷新机制 |
| 2.2 | 密码安全 | ✅ 已实现 | bcrypt 加密、密码强度验证 |
| 2.3 | 单设备登录 | ✅ 已实现 | SINGLE_LOGIN 机制 |
| 3. 授权安全 | |||
| 3.1 | RBAC 权限控制 | ✅ 已实现 | 完整的权限验证流程 |
| 3.2 | 数据权限隔离 | ✅ 已实现 | 数据范围控制 |
| 4. 数据安全 | |||
| 4.1 | SQL 注入防护 | ✅ 已实现 | 参数化查询 |
| 4.2 | 敏感信息脱敏 | ✅ 已实现 | 日志自动脱敏 |
| 4.3 | 输入验证 | ✅ 已实现 | 完整的验证规则 |
| 5. 传输安全 | |||
| 5.1 | CORS 配置 | ✅ 已实现 | 白名单机制 |
| 5.2 | HTTPS 支持 | 🔧 可优化 | 需要运维配置 |
| 6. 应用层安全 | |||
| 6.1 | XSS 防护 | ✅ 已实现 | 基础防护 |
| 6.2 | CSRF 防护 | ✅ 已实现 | 基础防护 |
| 6.3 | 验证码机制 | ✅ 已实现 | 图形验证码 |
| 7. Token 管理 | |||
| 7.1 | Token 生命周期 | ✅ 已实现 | Access/Refresh Token |
| 7.2 | Token 刷新机制 | ✅ 已实现 | 自动刷新 |
| 7.3 | Token 自动清理 | ✅ 已实现 | 定时清理过期 Token |
| 8. 前端安全 | |||
| 8.1 | Token 存储安全 | ✅ 已实现 | localStorage 存储 |
| 8.2 | 请求拦截 | ✅ 已实现 | Axios 拦截器 |
| 8.3 | 认证守卫 | ✅ 已实现 | 路由守卫 |
| 8.4 | SSE 连接安全 | ✅ 已实现 | Token 认证 |
| 9. 安全配置 | |||
| 9.1 | 环境变量 | ✅ 已实现 | 基础实现 |
| 9.2 | 生产环境配置 | 🔧 可优化 | 部分功能需确认 |
| 11. 文件上传安全 | |||
| 11.1 | 文件类型验证 | ✅ 已实现 | 白名单机制 |
| 11.2 | 文件内容验证 | ✅ 已实现 | 魔术数字、恶意内容检测 |
| 11.3 | 文件存储安全 | ✅ 已实现 | Hash 命名 |
| 11.4 | 文件访问控制 | ✅ 已实现 | 认证、权限验证 |
| 12. API 限流 | |||
| 12.1 | 基于 IP 的访问限制 | ✅ 已实现 | 部分 API |
| 12.2 | 请求频率控制 | 🔧 可优化 | 全局限流需完善 |
| 12.3 | 慢请求监控 | ✅ 已实现 | 自动监控 |
| 13. HTTP 安全头 | |||
| 13.1 | Helmet 安全头 | 🔧 可优化 | 配置已定义,需确认实现 |
| 13.2 | CORS 安全头 | ✅ 已实现 | 自定义请求头 |
| 14. 请求追踪 | |||
| 14.1 | 请求 ID 追踪 | ✅ 已实现 | correlation_id |
| 14.2 | 请求日志记录 | ✅ 已实现 | 分级日志 |
| 15. 错误处理 | |||
| 15.1 | 统一错误响应 | ✅ 已实现 | 统一格式 |
| 15.2 | 错误信息脱敏 | ✅ 已实现 | 生产环境不泄露 |
| 15.3 | 错误日志记录 | ✅ 已实现 | 完整记录 |
| 16. 前端安全增强 | |||
| 16.1 | 输入验证 | ✅ 已实现 | 基础验证 |
| 16.2 | XSS 防护 | ✅ 已实现 | React 自动转义 |
| 16.3 | Token 安全存储 | ✅ 已实现 | localStorage |
| 16.4 | 请求安全 | 🔧 可优化 | HTTPS 强制需配置 |
| 20. 安全审计 | |||
| 20.1 | 审计日志 | ✅ 已实现 | 完整记录 |
| 20.2 | 监控指标 | ✅ 已实现 | 基础监控 |
| 20.3 | 日志分析 | ✅ 已实现 | 基础查询 |
| 21. 账户安全增强 | |||
| 21.1 | 账户锁定机制 | 🔧 可优化 | 未实现 |
| 21.2 | 密码历史记录 | 🔧 可优化 | 未实现 |
| 21.3 | 密码过期策略 | 🔧 可优化 | 未实现 |
| 21.4 | 账户恢复安全 | ✅ 已实现 + 🔧 可优化 | 基础功能已实现 |
| 22. 密钥和配置管理 | |||
| 22.1 | 环境变量安全 | ✅ 已实现 + 🔧 可优化 | 基础实现,可引入密钥管理服务 |
| 22.2 | 密钥轮换策略 | 🔧 可优化 | 未实现 |
| 22.3 | 配置加密 | 🔧 可优化 | 未实现 |
| 23. 依赖包安全 | |||
| 23.1 | 依赖包漏洞扫描 | 🔧 可优化 | 需定期执行 |
| 23.2 | 依赖包更新策略 | 🔧 可优化 | 需建立流程 |
| 23.3 | 依赖包白名单 | 🔧 可优化 | 未实现 |
| 24. 数据库安全 | |||
| 24.1 | 数据库连接加密 | ✅ 已实现 | 生产环境 SSL |
| 24.2 | 数据库访问控制 | 🔧 可优化 | 需审查权限 |
| 24.3 | 数据库备份安全 | 🔧 可优化 | 需建立流程 |
| 25. 日志安全 | |||
| 25.1 | 日志访问控制 | 🔧 可优化 | 需实现权限控制 |
| 25.2 | 日志完整性保护 | 🔧 可优化 | 需实现数字签名 |
| 26. API 安全增强 | |||
| 26.1 | API 版本控制 | 🔧 可优化 | 未实现 |
| 26.2 | API 签名验证 | 🔧 可优化 | 未实现 |
| 26.3 | 请求签名 | 🔧 可优化 | 未实现 |
| 27. 安全配置检查清单 | |||
| 27.1 | 部署前安全检查 | 🔧 可优化 | 需建立流程 |
| 27.2 | 运行时安全检查 | 🔧 可优化 | 需建立流程 |
| 27.3 | 定期安全检查 | 🔧 可优化 | 需建立流程 |
| 28. 第三方服务安全 | |||
| 28.1 | 第三方 API 调用安全 | ✅ 已实现 + 🔧 可优化 | 基础实现 |
| 28.2 | 外部服务认证 | 🔧 可优化 | 需根据实际优化 |
| 29. 容器和部署安全 | |||
| 29.1 | Docker 安全 | 🔧 可优化 | 需根据部署方式优化 |
| 29.2 | 容器镜像安全 | 🔧 可优化 | 需根据部署方式优化 |
| 30. 安全培训和意识 | |||
| 30.1 | 开发人员安全培训 | 🔧 可优化 | 需建立培训体系 |
| 30.2 | 安全编码规范 | 🔧 可优化 | 需完善规范文档 |
图例说明:
状态: ✅ 已实现
// Access Token 载荷
{
id: user.id, // 用户ID
username: user.username, // 用户名
sessionid: sessionId, // 会话ID
jti: randomString // 随机盐值,增强安全性
}localStorage,通过 Authorization: Bearer <token> 发送tokens 表,包含过期时间、撤销状态、最后活动时间// 1. 从请求头提取 Token
const token = req.headers["authorization"]?.split(" ")[1];
// 2. 验证 Token 签名和过期时间
const decoded = jwt.verify(token, jwtConfig.secret);
// 3. 检查数据库中的有效性
const tokens = await Token.checkTokenValid(token, "access");
// 4. 更新最后活动时间
await Token.updateLastActiveTime(token);状态: ✅ 已实现
使用 bcryptjs 进行密码加密:
// 密码加密(盐值轮数:10)
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(password, salt);
// 密码验证
const isValid = bcrypt.compareSync(password, user.password);// 密码强度验证
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{6,16}$/;状态: ✅ 已实现
当 SINGLE_LOGIN=on 时,登录成功后:
// 单设备登录实现
if (process.env.SINGLE_LOGIN === "on") {
// 撤销其他会话(按客户端类型隔离)
await Token.revokeOtherSessionTokens(userId, sessionId, clientType);
// SSE 通知旧设备
sseManager.sendToUser(userId, {
type: "loginout",
data: { reason: "single_login_enforced" },
});
}状态: ✅ 已实现
// 1. 检查是否为超级管理员
if (isSuperAdmin(userRoles)) {
return next(); // 跳过权限验证
}
// 2. 获取用户权限码
const userPermissionCodes = await PermissionModel.getPermissionCodesByUserId(userId);
// 3. 验证权限
const hasPermission = requiredPermissions.some((permission) =>
userPermissionCodes.includes(permission)
);
// 4. 数据权限检查(可选)
if (checkDataPermission) {
// 基于 data_scope 和 data_scope_dept_ids 计算数据权限
}状态: ✅ 已实现
data_scope = 1data_scope = 2data_scope = 3data_scope = 4data_scope = 5// 权限层级检查:操作用户的权限层级必须 >= 被操作用户的权限层级
const currentUserLevel = await User.getUserMaxPermissionLevel(currentUserId);
const targetUserLevel = await User.getUserMaxPermissionLevel(targetUserId);
if (currentUserLevel < targetUserLevel) {
return forbidden("权限不足,无法执行此操作");
}状态: ✅ 已实现
所有数据库查询都使用参数化查询,防止 SQL 注入:
// ✅ 正确:使用参数化查询
const sql = "SELECT * FROM user WHERE id = ? AND username = ?";
const [rows] = await db.execute(sql, [userId, username]);
// ❌ 错误:字符串拼接(已禁止)
const sql = `SELECT * FROM user WHERE id = ${userId}`; // 危险!// 用户查询
const sql = `
SELECT * FROM user
WHERE username = ? AND is_delete = 0
`;
const [rows] = await db.execute(sql, [username]);
// 列表查询(带过滤条件)
const sql = `
SELECT * FROM user
WHERE department_id IN (${placeholders})
AND username LIKE ?
`;
const params = [...deptIds, `%${username}%`];
const [rows] = await db.execute(sql, params);状态: ✅ 已实现
系统自动对日志中的敏感信息进行脱敏:
| 字段类型 | 脱敏方式 | 示例 |
|---|---|---|
| password | 完全脱敏 | password123 → ****** |
| token | 完全脱敏 | abc123 → ****** |
| 邮箱脱敏 | user@example.com → us***@example.com |
|
| phone | 中间脱敏 | 13812345678 → 138****5678 |
| idCard | 中间脱敏 | 110101199001011234 → 110101********1234 |
| bankCard | 中间脱敏 | 6222021234567890 → 6222********7890 |
// 日志自动脱敏
const maskedData = dataMasker.maskObject({
username: "testuser",
password: "secret123", // 自动脱敏为 ******
email: "user@example.com", // 自动脱敏为 us***@example.com
phone: "13812345678", // 自动脱敏为 138****5678
});状态: ✅ 已实现
系统对所有用户输入进行严格验证:
// 用户名验证
validateUsername(username);
// 规则:3-20位,只能包含字母、数字、下划线
// 密码验证
validatePassword(password);
// 规则:6-20位字符
// 邮箱验证
validateEmail(email);
// 规则:标准邮箱格式
// 手机号验证
validateMobile(phone);
// 规则:1[3-9]开头的11位数字
// ID验证
validateId(id);
// 规则:大于0的整数// 登录验证
const usernameValidation = validateUsername(username);
if (!usernameValidation.isValid) {
return badRequest(usernameValidation.message);
}
const passwordValidation = validatePassword(password);
if (!passwordValidation.isValid) {
return badRequest(passwordValidation.message);
}状态: ✅ 已实现
const corsOptions = {
origin: function (origin, callback) {
// 开发环境:允许所有源
if (isDev || !origin) {
return callback(null, true);
}
// 生产环境:白名单验证
const allowedOrigins = config.cors?.allowedOrigins || [];
return allowedOrigins.includes(origin)
? callback(null, true)
: callback(new Error("Not allowed by CORS"));
},
credentials: true, // 允许携带凭证
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
allowedHeaders: [
"Origin",
"Content-Type",
"Authorization",
"Accept",
"X-Client-Type",
"X-Client-Page",
"X-Client-Id",
"X-File-Hash",
],
};状态: 🔧 可优化(需要运维配置)
状态: ✅ 已实现(基础防护)
状态: ✅ 已实现(基础防护)
状态: ✅ 已实现
// 生成验证码
const { captchaId, captchaImage } = await createCaptcha();
// 验证验证码
const { ok, reason } = await verifyCaptcha(captchaId, captcha);
if (!ok) {
return badRequest("验证码错误");
}状态: ✅ 已实现
状态: ✅ 已实现
// 1. Access Token 过期,返回 401
if (response.status === 401) {
// 2. 使用 Refresh Token 刷新
const result = await refreshToken(refreshToken);
// 3. 更新 Token
localStorage.setItem("access_token", result.accessToken);
localStorage.setItem("refresh_token", result.refreshToken);
// 4. 重试原请求
return retryOriginalRequest();
}isRefreshing 标志状态: ✅ 已实现
// 定时清理过期 Token(每小时执行一次)
const cleanupExpiredTokens = async () => {
const cleanedCount = await Token.cleanExpiredTokens();
logger.info(`清理了 ${cleanedCount} 个过期token`);
};状态: ✅ 已实现
// 清除认证信息
const clearAuth = () => {
localStorage.clear();
clearAllAuthData();
};状态: ✅ 已实现
// 自动添加 Token
axios.interceptors.request.use((config) => {
const token = localStorage.getItem("access_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器:处理 401 错误
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
handleTokenRefresh(error);
}
return Promise.reject(error);
}
);状态: ✅ 已实现
// AuthGuard 组件
const AuthGuard = ({ children }) => {
const { isLogin, validateAuth } = useAuth();
useEffect(() => {
if (!isLogin) {
validateAuth();
}
}, [isLogin]);
if (!isLogin) {
return <Navigate to="/login" />;
}
return children;
};状态: ✅ 已实现
// SSE 连接携带 Token
const eventSourceOptions = {
withCredentials: true,
headers: {
Authorization: `Bearer ${token}`,
"X-Client-Id": clientId,
},
};
const eventSource = new EventSourcePolyfill(url, eventSourceOptions);状态: ✅ 已实现(基础实现)
# JWT 密钥(必须保密)
JWT_SECRET=your-jwt-secret-key
REFRESH_SECRET=your-refresh-secret-key
# 单设备登录开关
SINGLE_LOGIN=on
# CORS 允许的源(生产环境)
CORS_ALLOWED_ORIGINS=https://www.example.com,https://example.com状态: 🔧 可优化(配置已定义,但部分功能未完全实现)
// config/env/production.js
security: {
helmet: true, // HTTP 安全头(配置已定义,需确认实现)
rateLimit: true, // 速率限制(配置已定义,需确认实现)
csrf: true, // CSRF 防护(配置已定义,需确认实现)
xssFilter: true, // XSS 过滤(配置已定义,需确认实现)
}状态: ✅ 已实现
系统严格限制允许上传的文件类型:
// 头像上传:仅允许图片格式
allowed: {
avatar: ["image/jpeg", "image/png", "image/gif", "image/webp"],
file: [
"image/jpeg", "image/png", "image/gif", "image/webp",
"application/pdf",
"application/msword",
"application/vnd.ms-excel",
"application/zip",
"text/plain"
]
}limits: {
avatarMaxSize: 5 * 1024 * 1024, // 头像:5MB
fileMaxSize: 50 * 1024 * 1024, // 文件:50MB
chunkMaxSize: 10 * 1024 * 1024, // 分块:10MB
}状态: ✅ 已实现
系统通过文件头的魔术数字(Magic Number)验证文件真实类型,防止文件扩展名伪造:
// 检测文件真实类型
const detectMimeType = (buffer) => {
// 检查文件头字节
if (buffer[0] === 0xff && buffer[1] === 0xd8) return "image/jpeg";
if (buffer[0] === 0x89 && buffer[1] === 0x50) return "image/png";
// ... 更多类型检测
};对文本文件进行恶意脚本检测:
const maliciousPatterns = [
"<script",
"javascript:",
"onerror=",
"onload=",
"exec(",
"eval(",
"document.cookie",
"base64_decode",
"system(",
"passthru(",
"shell_exec(",
"file_put_contents",
"fwrite(",
"fopen(",
"include(",
"require(",
];
// 检测文本文件中的恶意内容
if (isTextFile) {
const detectedPattern = maliciousPatterns.find((pattern) => fileContent.includes(pattern));
if (detectedPattern) {
return badRequest("文件内容包含不允许的代码或脚本");
}
}状态: ✅ 已实现
// 使用 Hash 值作为文件名
if (fileHash) {
filename = `${fileHash}${ext}`;
} else {
filename = `${userId}_${timestamp}_${originalName}`;
}状态: ✅ 已实现
../ 等)状态: ✅ 已实现(部分 API,如 AI 聊天接口)
系统对敏感 API 实施基于 IP 的访问次数限制:
// 统计 IP 访问次数
const accessCount = await AuditLogModel.getLogCount({
module_code: "aichat",
business_type: "deepseek",
action_type: "chat",
client_ip: userIp,
});
// 检查是否超过限制
if (accessCount >= MAX_VISITS) {
return badRequest("您的访问次数已超过限制,请稍后再试");
}状态: 🔧 可优化(配置已定义,但全局限流未完全实现)
// config/env/production.js
security: {
rateLimit: true, // 启用速率限制(配置已定义,需确认全局实现)
}状态: ✅ 已实现
系统自动监控慢请求,记录超过阈值的请求:
// 慢请求阈值:默认 1 秒
const SLOW_REQUEST_THRESHOLD = 1000;
// 记录慢请求
if (responseTime > SLOW_REQUEST_THRESHOLD) {
logger.warn("慢请求检测", {
method: req.method,
url: req.url,
responseTime,
userId: req.user?.id,
});
}状态: 🔧 可优化(配置已定义,需确认是否已集成 Helmet 中间件)
生产环境启用 Helmet 中间件,自动设置安全 HTTP 头:
// config/env/production.js
security: {
helmet: true, // 启用 Helmet(配置已定义,需确认实现)
}nosniff - 防止 MIME 类型嗅探DENY - 防止点击劫持1; mode=block - 启用 XSS 过滤器// 允许的自定义请求头
allowedHeaders: [
"Origin",
"Content-Type",
"Authorization",
"Accept",
"X-Client-Type",
"X-Client-Page",
"X-Client-Id",
"X-File-Hash",
];状态: ✅ 已实现
每个请求自动生成唯一的 correlation_id,用于追踪请求链路:
// 请求 ID 中间件
app.use(requestIdMiddleware);
// 自动生成 correlation_id
const correlationId =
req.headers["x-correlation-id"] || `req_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
// 设置到请求对象和全局变量
req.correlationId = correlationId;
global.correlationId = correlationId;correlation_id状态: ✅ 已实现
// 根据状态码和响应时间选择日志级别
if (statusCode >= 500) {
level = "error";
} else if (statusCode >= 400) {
level = "warn";
} else if (responseTime > SLOW_REQUEST_THRESHOLD) {
level = "warn"; // 慢请求记录为警告
} else {
level = "info";
}状态: ✅ 已实现
系统使用统一的错误响应格式,避免泄露敏感信息:
// 统一响应格式
{
success: false,
code: 400,
message: "请求参数错误", // 用户友好的错误消息
data: null,
timestamp: "2025-12-10T10:00:00.000Z"
}状态: ✅ 已实现
// 生产环境错误处理
if (process.env.NODE_ENV === "production") {
return serverError(req, res, "服务器内部错误");
} else {
// 开发环境可以返回详细错误信息
return serverError(req, res, err.message);
}状态: ✅ 已实现
所有错误都记录到日志系统(已脱敏):
// 错误日志包含
{
method: req.method,
url: req.url,
ip: req.ip,
userId: req.user?.id || "unauthenticated",
errorName: err.name,
errorMessage: err.message,
errorStack: err.stack, // 仅开发环境
statusCode: err.statusCode || 500,
}状态: ✅ 已实现(基础验证)
前端对所有用户输入进行验证:
// 表单验证
const validateForm = async () => {
try {
const values = await form.validateFields();
// 验证通过后提交
return values;
} catch (error) {
// 验证失败,阻止提交
return null;
}
};⚠️ 重要:客户端验证仅用于用户体验,所有验证必须在服务端进行。
状态: ✅ 已实现(基础防护)
React 默认转义所有输出,防止 XSS:
// ✅ 安全:React 自动转义
<div>{userInput}</div>
// ⚠️ 危险:使用 dangerouslySetInnerHTML 需要谨慎
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />建议在 HTTP 响应头中设置 CSP:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'状态: ✅ 已实现
// 页面关闭时清理敏感数据
window.addEventListener("beforeunload", () => {
// 清理敏感内存变量
clearSensitiveData();
});状态: 🔧 可优化(HTTPS 强制需要前端配置)
生产环境强制使用 HTTPS:
// 开发环境检查
if (process.env.NODE_ENV === "production" && !window.location.protocol.includes("https")) {
window.location.href = window.location.href.replace("http:", "https:");
}// Axios 请求超时配置
const axiosInstance = axios.create({
timeout: 30000, // 30秒超时
});-- 测试参数化查询是否有效
-- 尝试注入:' OR '1'='1
SELECT * FROM user WHERE username = ? AND password = ?
-- 参数:[' OR ''1''=''1', 'password']
-- 预期:查询失败或返回空结果<!-- 测试脚本注入 -->
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
javascript:alert('XSS').jpg 文件但内容是 PHP 脚本../ 访问其他目录// 1. 检查 Token 是否存在
const token = localStorage.getItem("access_token");
// 2. 检查 Token 格式
if (!token || !token.startsWith("Bearer ")) {
// Token 格式错误
}
// 3. 检查 Token 是否过期
try {
const decoded = jwt.verify(token, secret);
// Token 有效
} catch (error) {
if (error.name === "TokenExpiredError") {
// Token 已过期,需要刷新
}
}credentials: true 但 origin: '*'// 1. 检查请求来源
const origin = req.headers.origin;
console.log("请求来源:", origin);
// 2. 检查允许的源列表
const allowedOrigins = config.cors?.allowedOrigins || [];
console.log("允许的源:", allowedOrigins);
// 3. 检查是否匹配
if (!allowedOrigins.includes(origin)) {
console.error("CORS 拒绝:", origin);
}// 1. 检查文件类型
console.log("文件 MIME 类型:", req.file.mimetype);
console.log("允许的类型:", allowedMimeTypes);
// 2. 检查文件大小
console.log("文件大小:", req.file.size);
console.log("最大大小:", maxFileSize);
// 3. 检查文件内容
const fileBuffer = fs.readFileSync(req.file.path);
const detectedType = detectMimeType(fileBuffer);
console.log("检测到的类型:", detectedType);// 监控异常行为
const suspiciousActivities = ["大量失败登录", "异常 IP 访问", "权限异常变更", "高频 API 调用"];// 紧急撤销用户所有 Token
await Token.revokeAllUserTokens(userId);
// 通知用户
sseManager.sendToUser(userId, {
type: "loginout",
data: { reason: "security_incident" },
});// 封禁恶意 IP
await IPBlacklist.add(ipAddress, {
reason: "suspicious_activity",
duration: 24 * 60 * 60 * 1000, // 24小时
});状态: ✅ 已实现
系统记录所有关键操作:
状态: ✅ 已实现(基础监控)
// 异常登录告警
if (loginLocation !== userNormalLocation) {
sendAlert("异地登录告警", { userId, location });
}
// 权限提升告警
if (newPermissionLevel > oldPermissionLevel) {
sendAlert("权限提升告警", { userId, oldLevel, newLevel });
}
// 高频请求告警
if (requestCount > threshold) {
sendAlert("高频请求告警", { ip, count, threshold });
}状态: ✅ 已实现(基础查询功能)
系统提供日志查询接口,支持:
状态: 🔧 可优化(当前未实现)
当前系统可以增加账户锁定功能,防止暴力破解:
// 账户锁定策略
const ACCOUNT_LOCKOUT_CONFIG = {
maxFailedAttempts: 5, // 最大失败次数
lockoutDuration: 30 * 60, // 锁定时长(秒):30分钟
resetWindow: 15 * 60, // 重置窗口(秒):15分钟内失败次数累计
};
// 记录登录失败次数
await UserModel.recordFailedLogin(userId);
// 检查是否应该锁定
const shouldLock = await UserModel.shouldLockAccount(userId);
if (shouldLock) {
await UserModel.lockAccount(userId, lockoutDuration);
return badRequest("账户已被锁定,请稍后再试");
}状态: 🔧 可优化(当前未实现)
防止用户重复使用最近使用过的密码:
// 密码历史记录
const PASSWORD_HISTORY_COUNT = 5; // 记录最近5个密码
// 检查密码历史
const recentPasswords = await PasswordHistory.getRecentPasswords(userId, PASSWORD_HISTORY_COUNT);
const isReused = recentPasswords.some((history) =>
bcrypt.compareSync(newPassword, history.hashedPassword)
);
if (isReused) {
return badRequest("不能使用最近使用过的密码");
}
// 保存新密码到历史记录
await PasswordHistory.addPassword(userId, hashedPassword);状态: 🔧 可优化(当前未实现)
强制用户定期更换密码:
// 密码过期配置
const PASSWORD_EXPIRY_CONFIG = {
maxAge: 90 * 24 * 60 * 60 * 1000, // 90天
warningDays: 7, // 提前7天警告
};
// 检查密码是否过期
const passwordAge = Date.now() - user.passwordUpdatedAt;
if (passwordAge > PASSWORD_EXPIRY_CONFIG.maxAge) {
return unauthorized("密码已过期,请修改密码");
}
// 密码即将过期警告
if (
passwordAge >
PASSWORD_EXPIRY_CONFIG.maxAge - PASSWORD_EXPIRY_CONFIG.warningDays * 24 * 60 * 60 * 1000
) {
// 返回警告信息
return success(req, res, "密码即将过期,建议修改", {
passwordExpiryWarning: true,
daysRemaining: Math.ceil((PASSWORD_EXPIRY_CONFIG.maxAge - passwordAge) / (24 * 60 * 60 * 1000)),
});
}状态: ✅ 已实现(基础功能)+ 🔧 可优化(增强功能)
系统支持管理员重置密码,但可以增强安全性:
// 当前实现:管理员重置密码为初始密码
const resetPassword = async (req, res) => {
// 检查权限
// 重置为初始密码:123456
// 记录审计日志
};状态: ✅ 已实现(基础实现)+ 🔧 可优化(密钥管理服务)
系统使用 .env 文件存储敏感配置:
# .env 文件
JWT_SECRET=your-jwt-secret-key
REFRESH_SECRET=your-refresh-secret-key
DB_PASSWORD=your_password.env 文件应添加到 .gitignore// 密钥管理最佳实践
const secrets = {
jwtSecret:
process.env.JWT_SECRET ||
(() => {
throw new Error("JWT_SECRET is required");
})(),
dbPassword:
process.env.DB_PASSWORD ||
(() => {
throw new Error("DB_PASSWORD is required");
})(),
};状态: 🔧 可优化(当前未实现)
定期轮换密钥,降低密钥泄露风险:
// 密钥轮换配置
const KEY_ROTATION_CONFIG = {
jwtSecretRotationDays: 90, // JWT 密钥每90天轮换
refreshSecretRotationDays: 180, // Refresh 密钥每180天轮换
};
// 密钥轮换流程
const rotateJWTSecret = async () => {
// 1. 生成新密钥
const newSecret = crypto.randomBytes(64).toString("hex");
// 2. 更新环境变量(通过密钥管理服务)
await updateSecret("JWT_SECRET", newSecret);
// 3. 设置过渡期:新旧密钥同时有效
// 4. 通知所有用户重新登录(可选)
// 5. 过渡期结束后,移除旧密钥
};状态: 🔧 可优化(当前未实现)
对敏感配置进行加密存储:
// 配置加密
const crypto = require("crypto");
const encryptConfig = (value, key) => {
const cipher = crypto.createCipher("aes-256-cbc", key);
let encrypted = cipher.update(value, "utf8", "hex");
encrypted += cipher.final("hex");
return encrypted;
};
const decryptConfig = (encrypted, key) => {
const decipher = crypto.createDecipher("aes-256-cbc", key);
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
};状态: 🔧 可优化(需要定期执行扫描)
# 扫描依赖包漏洞
npm audit
# 自动修复可修复的漏洞
npm audit fix
# 生成漏洞报告
npm audit --json > audit-report.json状态: 🔧 可优化(需要建立更新流程)
package-lock.json 锁定版本// package.json
{
"dependencies": {
"express": "^4.18.0", // 允许补丁和次要版本更新
"jsonwebtoken": "8.5.1" // 锁定版本,手动更新
}
}状态: 🔧 可优化(当前未实现)
限制可安装的依赖包来源:
// .npmrc 配置
registry=https://registry.npmjs.org/
@scope:registry=https://registry.npmjs.org/状态: ✅ 已实现(生产环境已配置 SSL)
生产环境已启用 SSL:
// config/env/production.js
db: {
ssl: { rejectUnauthorized: true }, // 启用 SSL
}状态: 🔧 可优化(需要审查和优化数据库用户权限)
-- 创建只读用户
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'strong_password';
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';
-- 创建应用用户(只授予必要权限)
CREATE USER 'app_user'@'%' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE ON database_name.* TO 'app_user'@'%';状态: 🔧 可优化(需要建立备份流程和加密机制)
# 数据库备份脚本
mysqldump -u user -p database_name | \
gzip | \
openssl enc -aes-256-cbc -salt -k password > \
backup_$(date +%Y%m%d).sql.gz.enc状态: 🔧 可优化(需要实现日志访问权限控制)
// 日志访问控制中间件
const logAccessControl = (req, res, next) => {
// 检查用户权限
if (!req.user || !hasPermission(req.user, "log:read")) {
return forbidden(req, res, "无权访问日志");
}
next();
};状态: 🔧 可优化(当前未实现数字签名)
使用数字签名保护日志完整性:
// 日志签名
const crypto = require("crypto");
const signLog = (logEntry) => {
const signature = crypto
.createHmac("sha256", LOG_SIGNING_KEY)
.update(JSON.stringify(logEntry))
.digest("hex");
return {
...logEntry,
signature,
};
};
// 验证日志签名
const verifyLog = (logEntry) => {
const { signature, ...entry } = logEntry;
const expectedSignature = crypto
.createHmac("sha256", LOG_SIGNING_KEY)
.update(JSON.stringify(entry))
.digest("hex");
return signature === expectedSignature;
};状态: 🔧 可优化(当前未实现版本控制)
通过版本控制管理 API 变更:
// API 版本路由
app.use("/api/v1", v1Routes);
app.use("/api/v2", v2Routes);
// 版本兼容性
app.use("/api", (req, res, next) => {
// 检查 API 版本
const apiVersion = req.headers["api-version"] || "v1";
req.apiVersion = apiVersion;
next();
});状态: 🔧 可优化(当前未实现)
对敏感 API 实施签名验证:
// API 签名验证
const verifyAPISignature = (req, res, next) => {
const { signature, timestamp, nonce } = req.headers;
// 检查时间戳(防止重放攻击)
const now = Date.now();
if (Math.abs(now - timestamp) > 5 * 60 * 1000) {
return badRequest(req, res, "请求已过期");
}
// 验证签名
const expectedSignature = generateSignature(req, timestamp, nonce);
if (signature !== expectedSignature) {
return unauthorized(req, res, "签名验证失败");
}
next();
};状态: 🔧 可优化(当前未实现)
对关键请求实施签名验证:
// 请求签名生成
const generateRequestSignature = (method, url, body, timestamp, secret) => {
const message = `${method}${url}${JSON.stringify(body)}${timestamp}`;
return crypto.createHmac("sha256", secret).update(message).digest("hex");
};状态: 🔧 可优化(需要建立检查流程)
状态: 🔧 可优化(需要建立检查流程)
状态: 🔧 可优化(需要建立检查流程)
状态: ✅ 已实现(基础实现)+ 🔧 可优化(增强安全性)
// 第三方 API 调用示例
const callThirdPartyAPI = async (data) => {
const apiKey = process.env.THIRD_PARTY_API_KEY;
const signature = generateSignature(data, apiKey);
try {
const response = await axios.post(apiUrl, data, {
headers: {
Authorization: `Bearer ${apiKey}`,
"X-Signature": signature,
},
timeout: 10000, // 10秒超时
});
return response.data;
} catch (error) {
logger.error("第三方 API 调用失败", { error: error.message });
throw error;
}
};状态: 🔧 可优化(需要根据实际使用的第三方服务优化)
状态: 🔧 可优化(需要根据实际部署方式优化)
# Dockerfile 安全示例
FROM node:18-alpine # 使用 Alpine 最小镜像
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 设置工作目录
WORKDIR /app
# 复制文件
COPY --chown=nodejs:nodejs . .
# 切换到非 root 用户
USER nodejs
# 启动应用
CMD ["node", "index.js"]状态: 🔧 可优化(需要根据实际部署方式优化)
状态: 🔧 可优化(需要建立培训体系)
状态: 🔧 可优化(需要建立和完善编码规范文档)
安全任重道远