- 排除正常的应用路径(/api/admin/, /api/, /chat/, /gallery/, /settings/等) - 避免正常的网页访问被标记为可疑请求 - 只对真正可疑的路径(如/.env, /wp-admin等)进行标记
141 lines
3.4 KiB
TypeScript
141 lines
3.4 KiB
TypeScript
/**
|
||
* 请求监控和安全日志
|
||
* 用于记录可疑请求和异常行为
|
||
*/
|
||
|
||
interface SuspiciousRequest {
|
||
ip: string | null;
|
||
pathname: string;
|
||
method: string;
|
||
userAgent: string | null;
|
||
timestamp: Date;
|
||
reason: string;
|
||
}
|
||
|
||
// 可疑请求关键词
|
||
const SUSPICIOUS_PATTERNS = [
|
||
/wget|curl|nc |netcat|mkfifo|bash -i|sh -i/i,
|
||
/\.update|\.sh|\.py|\.pl|\.rb/i,
|
||
/exec|eval|system|shell|command/i,
|
||
/193\.142\.147\.209|94\.154\.35\.154/i, // 已知攻击者IP
|
||
];
|
||
|
||
// 可疑路径
|
||
const SUSPICIOUS_PATHS = [
|
||
"/.env",
|
||
"/.git/config",
|
||
"/admin",
|
||
"/wp-admin",
|
||
"/phpmyadmin",
|
||
"/.update",
|
||
"/tmp/",
|
||
];
|
||
|
||
/**
|
||
* 检查请求是否可疑
|
||
*/
|
||
export function isSuspiciousRequest(request: Request): {
|
||
suspicious: boolean;
|
||
reason?: string;
|
||
} {
|
||
const url = new URL(request.url);
|
||
const pathname = url.pathname;
|
||
const method = request.method;
|
||
|
||
// 排除正常的应用路径(避免误报)
|
||
const allowedPaths = [
|
||
"/api/admin/", // 管理后台API(正常功能)
|
||
"/api/", // 所有API路径(正常功能)
|
||
"/chat/", // 聊天页面
|
||
"/gallery/", // 相册页面
|
||
"/settings/", // 设置页面
|
||
"/login", // 登录页面
|
||
"/register", // 注册页面
|
||
];
|
||
|
||
// 如果是正常的应用路径,不标记为可疑
|
||
const isAllowedPath = allowedPaths.some(allowed => pathname.startsWith(allowed));
|
||
if (isAllowedPath) {
|
||
return { suspicious: false };
|
||
}
|
||
|
||
// 检查可疑路径
|
||
for (const pattern of SUSPICIOUS_PATHS) {
|
||
if (pathname.includes(pattern)) {
|
||
return {
|
||
suspicious: true,
|
||
reason: `Suspicious path: ${pathname}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 检查请求体中的可疑内容(仅对POST请求)
|
||
if (method === "POST") {
|
||
// 注意:这里不能直接读取body,因为body只能读取一次
|
||
// 实际检查应该在API路由中进行
|
||
}
|
||
|
||
// 检查User-Agent
|
||
const userAgent = request.headers.get("user-agent") || "";
|
||
for (const pattern of SUSPICIOUS_PATTERNS) {
|
||
if (pattern.test(userAgent)) {
|
||
return {
|
||
suspicious: true,
|
||
reason: `Suspicious User-Agent: ${userAgent.substring(0, 100)}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
return { suspicious: false };
|
||
}
|
||
|
||
/**
|
||
* 记录可疑请求
|
||
*/
|
||
export function logSuspiciousRequest(
|
||
request: Request,
|
||
reason: string
|
||
): void {
|
||
const clientIP = request.headers.get("x-forwarded-for")?.split(",")[0] ||
|
||
request.headers.get("x-real-ip") ||
|
||
"unknown";
|
||
const url = new URL(request.url);
|
||
|
||
const logEntry: SuspiciousRequest = {
|
||
ip: clientIP,
|
||
pathname: url.pathname,
|
||
method: request.method,
|
||
userAgent: request.headers.get("user-agent"),
|
||
timestamp: new Date(),
|
||
reason,
|
||
};
|
||
|
||
console.warn("[Security Alert] Suspicious request detected:", logEntry);
|
||
|
||
// 在生产环境中,这里可以发送到监控系统或日志服务
|
||
// 例如:发送到Sentry、LogRocket、或自定义日志服务
|
||
}
|
||
|
||
/**
|
||
* 验证请求体是否包含可疑内容
|
||
*/
|
||
export async function validateRequestBody(
|
||
body: string
|
||
): Promise<{ valid: boolean; reason?: string }> {
|
||
// 检查是否包含命令执行相关的关键词
|
||
for (const pattern of SUSPICIOUS_PATTERNS) {
|
||
if (pattern.test(body)) {
|
||
return {
|
||
valid: false,
|
||
reason: `Suspicious content detected in request body: ${pattern}`,
|
||
};
|
||
}
|
||
}
|
||
|
||
return { valid: true };
|
||
}
|
||
|
||
|
||
|
||
|