添加严格的域名访问限制
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Host头验证中间件
|
||||
* 用于防止Host Header注入攻击
|
||||
*/
|
||||
|
||||
interface HostValidationResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
allowedHost?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取允许的Host列表
|
||||
* 根据不同环境和端口配置返回相应的Host白名单
|
||||
*/
|
||||
function getAllowedHosts(): string[] {
|
||||
// 基础IP地址
|
||||
const baseIP = '10.79.97.17';
|
||||
const testIP = '172.16.0.55';
|
||||
const localhostIP = '127.0.0.1';
|
||||
|
||||
// 生产环境端口列表
|
||||
const productionPorts = ['51703', '51704', '51705', '51706', '51707', '51708'];
|
||||
|
||||
// 测试环境端口列表
|
||||
const testPorts = ['5173', '5174', '5175', '5176', '5177', '5178'];
|
||||
|
||||
const allowedHosts: string[] = [];
|
||||
|
||||
// 添加基础IP(不带端口)
|
||||
allowedHosts.push(baseIP, testIP, 'localhost', localhostIP);
|
||||
|
||||
// 添加生产环境的IP:端口组合
|
||||
productionPorts.forEach(port => {
|
||||
allowedHosts.push(`${baseIP}:${port}`);
|
||||
});
|
||||
|
||||
// 添加测试环境的IP:端口组合
|
||||
testPorts.forEach(port => {
|
||||
allowedHosts.push(`${testIP}:${port}`);
|
||||
allowedHosts.push(`localhost:${port}`);
|
||||
allowedHosts.push(`${localhostIP}:${port}`);
|
||||
});
|
||||
|
||||
// 开发环境额外允许的Host
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
allowedHosts.push('localhost:3000', '127.0.0.1:3000');
|
||||
}
|
||||
|
||||
return allowedHosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Host头是否在允许列表中
|
||||
* @param request - Remix Request对象
|
||||
* @returns 验证结果
|
||||
*/
|
||||
export function validateHost(request: Request): HostValidationResult {
|
||||
const host = request.headers.get('host');
|
||||
const referer = request.headers.get('referer');
|
||||
const userAgent = request.headers.get('user-agent');
|
||||
|
||||
// 获取允许的Host列表
|
||||
const allowedHosts = getAllowedHosts();
|
||||
|
||||
console.log('🔒 Host验证开始:', {
|
||||
host: host,
|
||||
referer: referer,
|
||||
userAgent: userAgent ? userAgent.substring(0, 50) + '...' : null,
|
||||
allowedHosts: allowedHosts
|
||||
});
|
||||
|
||||
// 1. 验证Host头是否存在
|
||||
if (!host) {
|
||||
console.error('❌ Host头缺失');
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Missing Host header'
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 验证Host头是否在允许列表中
|
||||
if (!allowedHosts.includes(host)) {
|
||||
console.error('❌ Host头不在允许列表中:', {
|
||||
host: host,
|
||||
allowedHosts: allowedHosts
|
||||
});
|
||||
return {
|
||||
valid: false,
|
||||
error: `Invalid Host header: ${host}`
|
||||
};
|
||||
}
|
||||
|
||||
// 3. 验证Referer头(如果存在)
|
||||
if (referer) {
|
||||
try {
|
||||
const refererUrl = new URL(referer);
|
||||
const refererHost = refererUrl.host;
|
||||
|
||||
if (!allowedHosts.includes(refererHost)) {
|
||||
console.error('❌ Referer头不在允许列表中:', {
|
||||
referer: referer,
|
||||
refererHost: refererHost,
|
||||
allowedHosts: allowedHosts
|
||||
});
|
||||
return {
|
||||
valid: false,
|
||||
error: `Invalid Referer header: ${refererHost}`
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Referer头格式无效:', referer);
|
||||
return {
|
||||
valid: false,
|
||||
error: `Malformed Referer header: ${referer}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Host验证通过:', host);
|
||||
return {
|
||||
valid: true,
|
||||
allowedHost: host
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Origin头是否在允许列表中
|
||||
* @param request - Remix Request对象
|
||||
* @returns 验证结果
|
||||
*/
|
||||
export function validateOrigin(request: Request): HostValidationResult {
|
||||
const origin = request.headers.get('origin');
|
||||
|
||||
if (!origin) {
|
||||
// Origin头不是必须的,某些请求(如直接访问)可能没有Origin头
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
try {
|
||||
const originUrl = new URL(origin);
|
||||
const originHost = originUrl.host;
|
||||
const allowedHosts = getAllowedHosts();
|
||||
|
||||
if (!allowedHosts.includes(originHost)) {
|
||||
console.error('❌ Origin头不在允许列表中:', {
|
||||
origin: origin,
|
||||
originHost: originHost,
|
||||
allowedHosts: allowedHosts
|
||||
});
|
||||
return {
|
||||
valid: false,
|
||||
error: `Invalid Origin header: ${originHost}`
|
||||
};
|
||||
}
|
||||
|
||||
console.log('✅ Origin验证通过:', origin);
|
||||
return { valid: true };
|
||||
} catch (error) {
|
||||
console.error('❌ Origin头格式无效:', origin);
|
||||
return {
|
||||
valid: false,
|
||||
error: `Malformed Origin header: ${origin}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整的请求验证
|
||||
* 包括Host、Referer、Origin头的验证
|
||||
* @param request - Remix Request对象
|
||||
* @returns 验证结果
|
||||
*/
|
||||
export function validateRequest(request: Request): HostValidationResult {
|
||||
// 1. 验证Host头
|
||||
const hostValidation = validateHost(request);
|
||||
if (!hostValidation.valid) {
|
||||
return hostValidation;
|
||||
}
|
||||
|
||||
// 2. 验证Origin头
|
||||
const originValidation = validateOrigin(request);
|
||||
if (!originValidation.valid) {
|
||||
return originValidation;
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为受保护的路由
|
||||
* 某些路由可能需要更严格的验证
|
||||
* @param pathname - 请求路径
|
||||
* @returns 是否为受保护路由
|
||||
*/
|
||||
export function isProtectedRoute(pathname: string): boolean {
|
||||
const protectedRoutes = [
|
||||
'/callback',
|
||||
'/api/oauth/token',
|
||||
'/api/oauth/userinfo',
|
||||
'/logout',
|
||||
'/admin'
|
||||
];
|
||||
|
||||
return protectedRoutes.some(route => pathname.startsWith(route));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录安全事件
|
||||
* @param event - 事件类型
|
||||
* @param details - 事件详情
|
||||
* @param request - 请求对象
|
||||
*/
|
||||
export function logSecurityEvent(
|
||||
event: 'host_validation_failed' | 'origin_validation_failed' | 'referer_validation_failed',
|
||||
details: string,
|
||||
request: Request
|
||||
) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const url = new URL(request.url);
|
||||
const clientIP = request.headers.get('x-forwarded-for') ||
|
||||
request.headers.get('x-real-ip') ||
|
||||
'unknown';
|
||||
|
||||
console.error(`🚨 安全事件: ${event}`, {
|
||||
timestamp: timestamp,
|
||||
url: url.pathname + url.search,
|
||||
host: request.headers.get('host'),
|
||||
referer: request.headers.get('referer'),
|
||||
origin: request.headers.get('origin'),
|
||||
userAgent: request.headers.get('user-agent'),
|
||||
clientIP: clientIP,
|
||||
details: details
|
||||
});
|
||||
|
||||
// TODO: 这里可以添加更复杂的日志记录逻辑
|
||||
// 比如写入数据库、发送告警邮件等
|
||||
}
|
||||
Reference in New Issue
Block a user