From fafd897588655fe8cd7fd2e048e7d90b63c44173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=BB=E5=8B=87=E7=A5=A5?= <237899745@qq.com> Date: Mon, 24 Nov 2025 10:25:10 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20=E5=A2=9E=E5=BC=BA=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E5=AE=89=E5=85=A8=E6=80=A7=E5=92=8C=E9=98=B2?= =?UTF-8?q?=E5=88=B7=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加验证码请求限流器(30次/10分钟,超限封锁30分钟) - 添加验证码请求间隔控制(最小3秒间隔) - 升级验证码复杂度:4位数字 → 6位字母数字混合 - 移除易混淆字符(I/l/O/0/1等) - 优化验证码显示参数(尺寸、干扰线等) 这些改进大幅提升了验证码的安全性,有效防止暴力破解和恶意刷新。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/server.js | 56 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/backend/server.js b/backend/server.js index 3d20dee..a526e50 100644 --- a/backend/server.js +++ b/backend/server.js @@ -436,6 +436,46 @@ const shareLimiter = new RateLimiter({ blockDuration: 20 * 60 * 1000 }); +// 创建验证码获取限流器(30次请求/10分钟,封锁30分钟) +const captchaLimiter = new RateLimiter({ + maxAttempts: 30, + windowMs: 10 * 60 * 1000, + blockDuration: 30 * 60 * 1000 +}); + +// 验证码最小请求间隔控制 +const CAPTCHA_MIN_INTERVAL = 3000; // 3秒 +const captchaLastRequest = new TTLCache(15 * 60 * 1000); // 15分钟自动清理 + +// 验证码防刷中间件 +function captchaRateLimitMiddleware(req, res, next) { + const clientKey = `captcha:${captchaLimiter.getClientKey(req)}`; + const now = Date.now(); + + // 最小时间间隔限制 + const lastRequest = captchaLastRequest.get(clientKey); + if (lastRequest && (now - lastRequest) < CAPTCHA_MIN_INTERVAL) { + return res.status(429).json({ + success: false, + message: '验证码请求过于频繁,请稍后再试' + }); + } + captchaLastRequest.set(clientKey, now, 15 * 60 * 1000); + + // 窗口内总次数限流 + const result = captchaLimiter.recordFailure(clientKey); + if (result.blocked) { + return res.status(429).json({ + success: false, + message: `验证码请求过多,请在 ${result.waitMinutes} 分钟后再试`, + blocked: true, + resetTime: result.resetTime + }); + } + + next(); +} + // 登录防爆破中间件 function loginRateLimitMiddleware(req, res, next) { const clientIP = loginLimiter.getClientKey(req); @@ -601,17 +641,17 @@ app.get('/api/health', (req, res) => { }); // 生成验证码API -app.get('/api/captcha', (req, res) => { +app.get('/api/captcha', captchaRateLimitMiddleware, (req, res) => { try { const captcha = svgCaptcha.create({ - size: 4, // 验证码长度 - noise: 2, // 干扰线条数 + size: 6, // 验证码长度 + noise: 3, // 干扰线条数 color: true, // 使用彩色 - background: '#f0f0f0', // 背景色 - width: 120, - height: 40, - fontSize: 50, - charPreset: '0123456789' // 只使用数字 + background: '#f7f7f7', // 背景色 + width: 140, + height: 44, + fontSize: 52, + charPreset: 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' // 去掉易混淆字符,字母+数字 }); // 将验证码文本存储在session中