230 lines
6.8 KiB
GDScript
230 lines
6.8 KiB
GDScript
extends Node
|
||
class_name RateLimiter
|
||
## 速率限制器
|
||
## 防止消息洪水攻击和垃圾消息
|
||
|
||
# 速率限制配置
|
||
var max_messages_per_window: int = 10
|
||
var time_window: float = 1.0 # 1秒窗口
|
||
var cleanup_interval: float = 60.0 # 1分钟清理间隔
|
||
|
||
# 客户端消息记录
|
||
var client_message_history: Dictionary = {}
|
||
|
||
# 清理定时器
|
||
var cleanup_timer: Timer
|
||
|
||
## 初始化速率限制器
|
||
func _ready():
|
||
"""初始化速率限制器"""
|
||
# 从配置获取参数
|
||
max_messages_per_window = SecurityConfig.get_config("network_security", "max_message_rate", 10)
|
||
time_window = SecurityConfig.get_config("network_security", "rate_limit_window", 1.0)
|
||
|
||
# 设置清理定时器
|
||
cleanup_timer = Timer.new()
|
||
cleanup_timer.wait_time = cleanup_interval
|
||
cleanup_timer.timeout.connect(_cleanup_old_records)
|
||
cleanup_timer.autostart = true
|
||
add_child(cleanup_timer)
|
||
|
||
print("RateLimiter initialized - Max: %d msgs/%s sec" % [max_messages_per_window, time_window])
|
||
|
||
## 检查是否允许消息
|
||
func is_message_allowed(client_id: String) -> bool:
|
||
"""
|
||
检查客户端是否允许发送消息
|
||
@param client_id: 客户端ID
|
||
@return: 是否允许
|
||
"""
|
||
var current_time = Time.get_unix_time_from_system()
|
||
|
||
# 获取或创建客户端记录
|
||
if not client_message_history.has(client_id):
|
||
client_message_history[client_id] = {
|
||
"messages": [],
|
||
"last_cleanup": current_time
|
||
}
|
||
|
||
var client_record = client_message_history[client_id]
|
||
var messages = client_record.messages
|
||
|
||
# 清理过期消息
|
||
_cleanup_client_messages(client_id, current_time)
|
||
|
||
# 检查是否超过限制
|
||
if messages.size() >= max_messages_per_window:
|
||
print("WARNING: Rate limit exceeded for client: " + client_id + " (" + str(messages.size()) + " messages)")
|
||
return false
|
||
|
||
# 记录新消息
|
||
messages.append(current_time)
|
||
return true
|
||
|
||
## 清理客户端的过期消息记录
|
||
func _cleanup_client_messages(client_id: String, current_time: float):
|
||
"""
|
||
清理客户端的过期消息记录
|
||
@param client_id: 客户端ID
|
||
@param current_time: 当前时间
|
||
"""
|
||
if not client_message_history.has(client_id):
|
||
return
|
||
|
||
var client_record = client_message_history[client_id]
|
||
var messages = client_record.messages
|
||
var cutoff_time = current_time - time_window
|
||
|
||
# 移除过期消息
|
||
var valid_messages = []
|
||
for timestamp in messages:
|
||
if timestamp > cutoff_time:
|
||
valid_messages.append(timestamp)
|
||
|
||
client_record.messages = valid_messages
|
||
client_record.last_cleanup = current_time
|
||
|
||
## 清理所有过期记录
|
||
func _cleanup_old_records():
|
||
"""清理所有客户端的过期记录"""
|
||
var current_time = Time.get_unix_time_from_system()
|
||
var cleaned_clients = 0
|
||
var removed_clients = []
|
||
|
||
for client_id in client_message_history:
|
||
var client_record = client_message_history[client_id]
|
||
|
||
# 如果客户端超过5分钟没有活动,移除记录
|
||
if current_time - client_record.last_cleanup > 300.0:
|
||
removed_clients.append(client_id)
|
||
else:
|
||
# 清理过期消息
|
||
_cleanup_client_messages(client_id, current_time)
|
||
cleaned_clients += 1
|
||
|
||
# 移除不活跃的客户端记录
|
||
for client_id in removed_clients:
|
||
client_message_history.erase(client_id)
|
||
|
||
if removed_clients.size() > 0:
|
||
print("Rate limiter cleaned up %d inactive clients, %d active clients remain" % [removed_clients.size(), cleaned_clients])
|
||
|
||
## 重置客户端限制
|
||
func reset_client_limit(client_id: String):
|
||
"""
|
||
重置客户端的速率限制(用于特殊情况)
|
||
@param client_id: 客户端ID
|
||
"""
|
||
if client_message_history.has(client_id):
|
||
client_message_history[client_id].messages.clear()
|
||
print("Rate limit reset for client: " + client_id)
|
||
|
||
## 获取客户端消息统计
|
||
func get_client_stats(client_id: String) -> Dictionary:
|
||
"""
|
||
获取客户端的消息统计
|
||
@param client_id: 客户端ID
|
||
@return: 统计信息
|
||
"""
|
||
if not client_message_history.has(client_id):
|
||
return {
|
||
"message_count": 0,
|
||
"remaining_quota": max_messages_per_window,
|
||
"window_reset_time": 0
|
||
}
|
||
|
||
var current_time = Time.get_unix_time_from_system()
|
||
_cleanup_client_messages(client_id, current_time)
|
||
|
||
var client_record = client_message_history[client_id]
|
||
var message_count = client_record.messages.size()
|
||
var remaining_quota = max(0, max_messages_per_window - message_count)
|
||
|
||
# 计算窗口重置时间
|
||
var oldest_message_time = 0.0
|
||
if client_record.messages.size() > 0:
|
||
oldest_message_time = client_record.messages[0]
|
||
|
||
var window_reset_time = oldest_message_time + time_window
|
||
|
||
return {
|
||
"message_count": message_count,
|
||
"remaining_quota": remaining_quota,
|
||
"window_reset_time": window_reset_time,
|
||
"current_time": current_time
|
||
}
|
||
|
||
## 获取全局统计
|
||
func get_global_stats() -> Dictionary:
|
||
"""
|
||
获取全局速率限制统计
|
||
@return: 全局统计信息
|
||
"""
|
||
var total_clients = client_message_history.size()
|
||
var active_clients = 0
|
||
var total_messages = 0
|
||
var current_time = Time.get_unix_time_from_system()
|
||
|
||
for client_id in client_message_history:
|
||
var client_record = client_message_history[client_id]
|
||
if current_time - client_record.last_cleanup < 60.0: # 1分钟内活跃
|
||
active_clients += 1
|
||
total_messages += client_record.messages.size()
|
||
|
||
return {
|
||
"total_clients": total_clients,
|
||
"active_clients": active_clients,
|
||
"total_messages_in_window": total_messages,
|
||
"max_rate": max_messages_per_window,
|
||
"time_window": time_window
|
||
}
|
||
|
||
## 设置速率限制参数
|
||
func set_rate_limit(max_messages: int, window_seconds: float):
|
||
"""
|
||
动态设置速率限制参数
|
||
@param max_messages: 最大消息数
|
||
@param window_seconds: 时间窗口(秒)
|
||
"""
|
||
max_messages_per_window = max_messages
|
||
time_window = window_seconds
|
||
|
||
print("Rate limit updated - Max: %d msgs/%s sec" % [max_messages, window_seconds])
|
||
|
||
## 检查是否为可疑活动
|
||
func is_suspicious_activity(client_id: String) -> bool:
|
||
"""
|
||
检查客户端是否有可疑活动
|
||
@param client_id: 客户端ID
|
||
@return: 是否可疑
|
||
"""
|
||
var stats = get_client_stats(client_id)
|
||
|
||
# 如果消息数量接近限制,认为可疑
|
||
if stats.message_count >= max_messages_per_window * 0.8:
|
||
return true
|
||
|
||
# 检查消息发送模式是否异常(过于规律可能是机器人)
|
||
if client_message_history.has(client_id):
|
||
var messages = client_message_history[client_id].messages
|
||
if messages.size() >= 3:
|
||
var intervals = []
|
||
for i in range(1, messages.size()):
|
||
intervals.append(messages[i] - messages[i-1])
|
||
|
||
# 如果所有间隔都非常相似(差异小于0.1秒),可能是机器人
|
||
if intervals.size() >= 2:
|
||
var avg_interval = 0.0
|
||
for interval in intervals:
|
||
avg_interval += interval
|
||
avg_interval /= intervals.size()
|
||
|
||
var variance = 0.0
|
||
for interval in intervals:
|
||
variance += (interval - avg_interval) * (interval - avg_interval)
|
||
variance /= intervals.size()
|
||
|
||
if variance < 0.01: # 方差很小,模式过于规律
|
||
return true
|
||
|
||
return false |