🚀 增强验证码安全性和防刷机制

- 添加验证码请求限流器(30次/10分钟,超限封锁30分钟)
- 添加验证码请求间隔控制(最小3秒间隔)
- 升级验证码复杂度:4位数字 → 6位字母数字混合
- 移除易混淆字符(I/l/O/0/1等)
- 优化验证码显示参数(尺寸、干扰线等)

这些改进大幅提升了验证码的安全性,有效防止暴力破解和恶意刷新。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-24 10:25:10 +08:00
parent 46fb4d0fd0
commit fafd897588

View File

@@ -436,6 +436,46 @@ const shareLimiter = new RateLimiter({
blockDuration: 20 * 60 * 1000 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) { function loginRateLimitMiddleware(req, res, next) {
const clientIP = loginLimiter.getClientKey(req); const clientIP = loginLimiter.getClientKey(req);
@@ -601,17 +641,17 @@ app.get('/api/health', (req, res) => {
}); });
// 生成验证码API // 生成验证码API
app.get('/api/captcha', (req, res) => { app.get('/api/captcha', captchaRateLimitMiddleware, (req, res) => {
try { try {
const captcha = svgCaptcha.create({ const captcha = svgCaptcha.create({
size: 4, // 验证码长度 size: 6, // 验证码长度
noise: 2, // 干扰线条数 noise: 3, // 干扰线条数
color: true, // 使用彩色 color: true, // 使用彩色
background: '#f0f0f0', // 背景色 background: '#f7f7f7', // 背景色
width: 120, width: 140,
height: 40, height: 44,
fontSize: 50, fontSize: 52,
charPreset: '0123456789' // 只使用数字 charPreset: 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' // 去掉易混淆字符,字母+数字
}); });
// 将验证码文本存储在session中 // 将验证码文本存储在session中