ai-chatbot/lib/security/rate-limiter.ts
Your Name a8abed6b5b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: 添加多层安全防护机制
- 添加速率限制中间件,防止API滥用和DDoS攻击
- 添加可疑请求检测,基于行为模式识别攻击
- 添加输入清理和验证,防止命令注入和XSS攻击
- 更新middleware.ts,集成所有安全功能
- 添加fail2ban安装脚本,防止SSH暴力破解
- 不依赖IP黑名单,适用于所有攻击者

安全功能:
- API速率限制:每分钟100次请求
- 认证速率限制:每分钟5次请求
- 自动检测可疑请求模式
- 输入清理和验证
- 详细的安全日志记录
2025-12-28 22:45:13 +08:00

121 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 速率限制中间件
* 使用内存存储(生产环境建议使用Redis)
*/
interface RateLimitStore {
[key: string]: {
count: number;
resetTime: number;
};
}
class RateLimiter {
private store: RateLimitStore = {};
private windowMs: number;
private maxRequests: number;
constructor(windowMs: number = 60000, maxRequests: number = 100) {
this.windowMs = windowMs;
this.maxRequests = maxRequests;
// 定期清理过期记录
setInterval(() => this.cleanup(), this.windowMs);
}
/**
* 检查是否超过速率限制
*/
check(identifier: string): { allowed: boolean; remaining: number; resetTime: number } {
const now = Date.now();
const record = this.store[identifier];
if (!record || now > record.resetTime) {
// 创建新记录
this.store[identifier] = {
count: 1,
resetTime: now + this.windowMs,
};
return {
allowed: true,
remaining: this.maxRequests - 1,
resetTime: now + this.windowMs,
};
}
if (record.count >= this.maxRequests) {
return {
allowed: false,
remaining: 0,
resetTime: record.resetTime,
};
}
record.count++;
return {
allowed: true,
remaining: this.maxRequests - record.count,
resetTime: record.resetTime,
};
}
/**
* 清理过期记录
*/
private cleanup(): void {
const now = Date.now();
for (const key in this.store) {
if (this.store[key].resetTime < now) {
delete this.store[key];
}
}
}
}
// 创建不同级别的速率限制器
export const rateLimiters = {
// 通用API限制:每分钟100次请求
api: new RateLimiter(60000, 100),
// 严格限制:每分钟10次请求(用于敏感操作)
strict: new RateLimiter(60000, 10),
// 登录限制:每分钟5次请求
auth: new RateLimiter(60000, 5),
// IP限制:每分钟50次请求
ip: new RateLimiter(60000, 50),
};
/**
* 获取客户端标识符(IP地址)
*/
export function getClientIdentifier(request: Request): string {
// 优先使用x-forwarded-for(考虑代理)
const forwarded = request.headers.get("x-forwarded-for");
if (forwarded) {
return forwarded.split(",")[0].trim();
}
// 使用x-real-ip
const realIP = request.headers.get("x-real-ip");
if (realIP) {
return realIP;
}
// 降级到unknown
return "unknown";
}
/**
* 速率限制检查函数
*/
export function checkRateLimit(
request: Request,
limiter: RateLimiter = rateLimiters.api
): { allowed: boolean; remaining: number; resetTime: number } {
const identifier = getClientIdentifier(request);
return limiter.check(identifier);
}